The InnoDB buffer pool is the core of MySQL performance. It is where data and indexes are cached, where changes are prepared before flushing, and where most queries spend their time. Understanding how it works is essential for reliable tuning.
This article explains buffer pool internals, how pages flow through it, and step-by-step ways to size and tune it on a typical RHEL/Rocky Linux system.
1. What the InnoDB buffer pool actually does
InnoDB stores data and indexes in fixed-size pages (usually 16 KB). The buffer pool is an in-memory cache of these pages plus some metadata. Every read or write goes through the buffer pool:
┌──────────────────────────────────────────────┐
│ InnoDB Buffer Pool │
├──────────────────────────────────────────────┤
│ Cached data pages (reads & writes) │
│ Cached index pages │
│ Change buffer (secondary index changes) │
│ Adaptive hash index (optional) │
└──────────────────────────────────────────────┘
Key roles:
- Read cache: Avoids disk I/O by serving pages from memory.
- Write buffer: Modifies pages in memory and writes them to disk later.
- Concurrency control: Coordinates access with latches and locks.
2. How pages flow through the buffer pool
InnoDB uses a variation of LRU (least recently used) with a split list: a “young” sublist and an “old” sublist. This helps avoid large scans evicting the working set.
┌──────────────────────────────────────────────┐
│ LRU List (simplified) │
├───────────────┬─────────────────────────────┤
│ Young sublist │ Old sublist │
│ (hot pages) │ (recently read pages) │
└───────────────┴─────────────────────────────┘
High-level flow:
- Query needs a page.
- If in buffer pool, it is a cache hit and the page is moved towards the young end.
- If not, InnoDB loads it from disk into the old sublist.
- Pages in the old sublist that are not re-used soon are candidates for eviction.
Dirty pages (modified but not yet flushed) are tracked separately on a flush list. They must be written to disk before eviction.
3. Sizing the buffer pool step by step
The single most important setting is innodb_buffer_pool_size. Oversizing it will cause swapping and kill performance; undersizing it will cause constant disk I/O.
3.1 Gather memory information
On RHEL/Rocky:
free -m
# Or for more detail
cat /proc/meminfo | egrep "MemTotal|MemFree|Buffers|Cached"
Also consider what else runs on the host: OS, MySQL overhead, other services.
3.2 Derive an initial value
- Dedicated MySQL server: start with 60–70% of RAM.
- Shared server: be more conservative (30–40% or less).
Example: 64 GB RAM, dedicated MySQL:
- Target buffer pool ≈ 0.65 × 64 GB ≈ 42 GB.
In /etc/my.cnf:
[mysqld]
innodb_buffer_pool_size = 42G
Restart MySQL after changing this setting (unless you plan to use dynamic resizing; see below).
3.3 Split into multiple instances
For large buffer pools, multiple instances can reduce latch contention:
[mysqld]
innodb_buffer_pool_size = 42G
innodb_buffer_pool_instances = 8
Practical guidance:
- Below ~4 GB, multiple instances rarely help.
- Above ~8–10 GB, use 4–8 instances, adjusting based on workload.
4. Monitoring buffer pool efficiency
4.1 Check cache hit ratio
Use SHOW GLOBAL STATUS to derive a rough hit ratio:
mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
Key counters:
Innodb_buffer_pool_read_requests: logical reads from buffer pool.Innodb_buffer_pool_reads: reads that had to go to disk.
Approximate hit ratio:
hit_ratio = 1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)
As a rule of thumb, aim for > 99% for OLTP workloads. If it is much lower and the server still has free RAM, consider increasing the buffer pool (carefully, and only if the OS will not swap).
4.2 Inspect buffer pool statistics
Use InnoDB engine status:
mysql -e "SHOW ENGINE INNODB STATUS\G" | egrep "BUFFER POOL AND MEMORY|Buffer pool size|Database pages|Free buffers|Modified db pages" -A5
Look at:
- Buffer pool size: total pages.
- Database pages: used pages.
- Free buffers: how much headroom is left.
- Modified db pages: how many are dirty.
5. Dynamic buffer pool resizing
Modern MySQL allows changing innodb_buffer_pool_size online. This is useful but must be done gradually to avoid memory pressure.
5.1 Increase size online
Example: increase from 32G to 40G:
mysql -e "SET GLOBAL innodb_buffer_pool_size = 40 * 1024 * 1024 * 1024;"
Best practices:
- Increase in small steps (e.g. 10–20%).
- After each step, monitor
free -mand swap usage. - Update
/etc/my.cnfto make the change persistent.
5.2 Decrease size online
Decreasing forces InnoDB to evict pages. This can be disruptive if you shrink too fast. Decrease in small increments and monitor performance and I/O.
6. Controlling I/O pressure from the buffer pool
The buffer pool interacts closely with background flushing. If dirty pages accumulate faster than they are flushed, checkpoints lag and sudden spikes of I/O can appear.
6.1 Key parameters
innodb_io_capacity: InnoDB's view of the IOPS your storage can sustain.innodb_io_capacity_max: upper bound InnoDB can use during bursts.innodb_flush_neighbors: how aggressively adjacent pages are flushed.
Example baseline for decent SSD storage:
[mysqld]
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
innodb_flush_neighbors = 0
Adjust these based on actual storage benchmarks and monitoring. Too low, and dirty pages accumulate; too high, and you may saturate disks.
7. Avoiding common buffer pool pitfalls
7.1 Oversizing and swap
Never size the buffer pool so large that the OS starts swapping. Swap activity destroys latency-sensitive workloads.
Monitor:
vmstat 5
# Check swap usage
free -m
If you see non-zero swap usage increasing over time, reconsider buffer pool size and other memory consumers.
7.2 Large scans polluting the cache
Long-running full scans can push out hot pages. InnoDB's split LRU mitigates this, but bad query patterns still hurt.
Best practices:
- Index critical queries to avoid full table scans.
- Run heavy reporting on replicas or off-peak where possible.
- Use
EXPLAINto identify queries that scan large ranges.
7.3 Ignoring buffer pool warmup
After a restart, the buffer pool is cold and performance often degrades until it is warmed up. Use buffer pool dump and load to preserve hot pages across restarts.
[mysqld]
innodb_buffer_pool_dump_at_shutdown = 1
innodb_buffer_pool_load_at_startup = 1
On clean shutdown, InnoDB records the most frequently used pages. On startup, it reloads them, reducing the warmup period.
8. Step-by-step tuning process
When tuning an existing system, follow a structured approach:
- Measure current state
- Record buffer pool size, instances, hit ratio, dirty pages, and I/O stats.
- Capture workload characteristics (QPS, peak times).
- Check memory headroom
- Use
free -mandvmstatto confirm no swapping. - Estimate how much RAM you can safely allocate to InnoDB.
- Use
- Adjust buffer pool size
- Increase or decrease in small steps.
- Update
/etc/my.cnfand plan restarts during maintenance windows.
- Tune I/O capacity
- Measure disk performance with tools like
fio(outside MySQL). - Set
innodb_io_capacityandinnodb_io_capacity_maxaccordingly.
- Measure disk performance with tools like
- Stabilise and observe
- Monitor over several days, including peak periods.
- Revisit application queries and indexing once the buffer pool is stable.
This article offers general technical guidance. Validate all configurations in a safe environment before applying them to production.
With a clear view of how the InnoDB buffer pool works, you can make deliberate, low-risk tuning changes instead of guessing. Start by sizing it conservatively, monitor behaviour under real load, and iterate. Combined with good indexing and query design, a well-tuned buffer pool is one of the most effective levers for sustained MySQL performance.

