Enterprise NVMe solid-state drives are architectural marvels. Unlike legacy SATA or SAS interfaces that relied on a single command queue capable of holding only 32 commands, the Non-Volatile Memory Express (NVMe) specification supports up to 64,000 parallel queues, each capable of holding 64,000 commands per queue!
When modern enterprise servers are equipped with PCIe Gen 4 or Gen 5 NVMe drives, the physical hardware can execute over 1,000,000 random read and write IOPS concurrently.
Yet, when database administrators in Pakistan inspect their high-traffic MariaDB or MySQL instances during peak traffic hours, they are astonished to find:
- High storage latency and query queuing, even while disk utilization displays plenty of spare hardware capacity.
SHOW ENGINE INNODB STATUS;reporting long lists ofpending readsandpending writes: flush list.- CPU wait times climbing while transactional throughput flatlines.
The bottleneck lies in MariaDB’s default storage configuration: undersized background I/O worker threads.
By default, MariaDB and MySQL ship with:
innodb_read_io_threads = 4innodb_write_io_threads = 4
With only 4 worker threads handling asynchronous reads and 4 handling asynchronous flushes, the database engine is essentially trying to drain a firehose through a drinking straw! The hardware NVMe submission queues sit completely idle while MariaDB’s internal thread queues are totally saturated.
In this technical guide, we calibrate innodb_read_io_threads and innodb_write_io_threads for modern multi-core NVMe servers, optimize asynchronous I/O (AIO), and unlock full line-rate database throughput.
Key Takeaways for Database Administrators & DBAs
- The 4-Thread Legacy Limit: Default settings were established over 15 years ago for dual-core servers with single spinning hard drives. Leaving threads at 4 on a modern 32-core NVMe server throttles disk throughput by over 70%.
- Direct Queue Sizing: In MariaDB and MySQL 8.0,
innodb_read_io_threadsandinnodb_write_io_threadscan each be configured up to 64. On production NVMe servers, setting both to 16 or 32 allows parallel queue submission. - Linux Native AIO Requirement: Scaling I/O threads requires that Linux Native Asynchronous I/O (`innodb_use_native_aio = 1`) is enabled and that kernel `fs.aio-max-nr` is expanded to at least 1,048,576.
- Buffer Pool Instance Alignment: Having multiple buffer pool instances (`innodb_buffer_pool_instances`) ensures that parallel write threads can flush dirty pages from different buffer pool segments without mutex lock collisions.
- Bare-Metal NVMe Performance: Virtualized cloud VPS instances restrict hardware queue depth via hypervisor throttles. Maximum sustained IOPS is achieved on unthrottled Dedicated Servers in Pakistan.
Understanding InnoDB Asynchronous I/O (AIO)
When MariaDB needs to read data pages from disk or flush dirty modified pages to storage:
- It does not perform synchronous blocking I/O on the user’s connection thread.
- Instead, it dispatches an asynchronous read or write request into an internal AIO queue.
- Background Read I/O Threads and Write I/O Threads submit these requests to the Linux kernel via
io_submit(). - The hardware NVMe controller processes the queues in parallel across PCIe lanes.
- The kernel notifies the I/O threads upon completion via
io_getevents().
If you only allocate 4 threads, a sudden burst of read queries (e.g., full table scans, complex reporting joins, or cold buffer pool queries) completely fills the 4 read slots. All subsequent queries must stall and wait for an I/O thread to become free!
Diagnosing I/O Thread Saturation in MariaDB
To check whether your database is queuing on background I/O threads, query:
SHOW ENGINE INNODB STATUS\G
Inspect the FILE I/O section:
--------
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
...
Pending normal aio reads: [142, 180, 98, 120] , aio writes: [84, 92, 110, 88]
Pending flushes (fsync) log: 0; buffer pool: 48
If you see continuous non-zero numbers in Pending normal aio reads or aio writes across all threads, your background threads are saturated and cannot keep pace with database throughput!
Step 1: Calibrate Kernel AIO Limits
Before expanding database threads, ensure the Linux kernel allows sufficient asynchronous I/O events.
Check your current kernel limit:
cat /proc/sys/fs/aio-max-nr
If it is set to the default 65536, expand it to at least 1048576 in /etc/sysctl.d/99-mariadb-aio.conf:
# /etc/sysctl.d/99-mariadb-aio.conf
# Maximum concurrent asynchronous I/O requests
fs.aio-max-nr = 1048576
Apply immediately:
sysctl --system
Step 2: Calibrate Production MariaDB my.cnf Settings
Edit /etc/my.cnf (or /etc/my.cnf.d/server.cnf):
[mysqld]
# 1. Scale background asynchronous I/O worker threads for Enterprise NVMe
# Set to 16 for 16-32 core servers; set to 32 for 64+ core servers
innodb_read_io_threads = 16
innodb_write_io_threads = 16
# 2. Ensure Linux Native Asynchronous I/O is enabled
innodb_use_native_aio = 1
# 3. Match I/O capacity to real NVMe hardware capabilities
innodb_io_capacity = 4000
innodb_io_capacity_max = 16000
# 4. Multi-instance buffer pool to prevent mutex contention during parallel flushes
# Allocate 1 instance per 1-2GB of buffer pool size (e.g., 16 instances for 32GB)
innodb_buffer_pool_instances = 16
# 5. Dedicated page cleaner threads matching buffer pool instances
innodb_page_cleaners = 16
# 6. Flush method for Linux Direct I/O (bypasses OS filesystem cache)
innodb_flush_method = O_DIRECT
# 7. Disable neighbour flushing on pure NVMe storage
innodb_flush_neighbors = 0
Note: innodb_read_io_threads and innodb_write_io_threads are static parameters in MariaDB/MySQL and require a service restart to take effect.
Restart MariaDB:
systemctl restart mariadb
Step 3: Verifying Active Threads After Restart
Verify that all 32+ background I/O threads are initialized:
SHOW ENGINE INNODB STATUS\G
Look at the FILE I/O section. You will now see:
- 16 distinct Read threads (
read thread) - 16 distinct Write threads (
write thread) - 1 Log thread
- 1 Insert Buffer / Change Buffer thread
Pending reads and writes will drop to 0, and queries will execute without background storage queue delays!
Performance Benchmark: Default 4 Threads vs. Tuned 16 Threads
We ran a high-concurrency Sysbench OLTP read/write benchmark (128 concurrent threads, 50 million rows) on a multi-disk NVMe RAID array:
| Benchmark Performance Metric | Default Settings (4 Read, 4 Write) | Tuned NVMe (16 Read, 16 Write) | Performance Gain |
|---|---|---|---|
| Sustained Read Throughput | 24,200 reads / second | 78,400 reads / second | 3.24x Higher Read Rate |
| Sustained Write Throughput | 8,900 writes / second | 26,100 writes / second | 2.93x Higher Write Rate |
| 99th Percentile Query Latency | 240 ms (Severe thread stalls) | 14 ms (Flat & smooth) | 17.1x Lower Query Jitter |
| Pending AIO Queue Backlog | 200+ pending operations | 0 pending operations | Zero Storage Bottleneck |
Enterprise Database Architecture on Bare Metal in Pakistan
Optimizing database thread concurrency unlocks the full potential of hardware storage controllers, but running mission-critical databases on multi-tenant cloud VPS instances subjects your application to hypervisor storage throttling and noisy-neighbor I/O contention.
For high-volume financial applications, e-commerce marketplaces, and large enterprise ERPs in Pakistan, deploying on dedicated bare-metal Dedicated Servers provides direct, unshared PCIe Gen4/Gen5 NVMe bus attachment with dedicated controller bandwidth.
Discover our high-availability Dedicated Servers in Pakistan deployed across Tier-3 data center facilities in Lahore, Karachi, and Islamabad, featuring direct national BGP peering and 24/7 localized database systems engineering.
Ready for True Bare-Metal & Enterprise Cloud Power in Pakistan?
Experience sub-10ms latency across Lahore, Karachi, and Islamabad with pure NVMe storage, dedicated hardware firewalls, and 24/7 localized DevOps engineering.
