Bali, Indonesia
Insights

How to Check If the InnoDB Buffer Pool Is Too Small for Your Site's Database

October 28, 2026
0:00
When a site slows down and the server graphs show steady disk reads, the usual advice is to raise innodb_buffer_pool_size. Sometimes that is the fix. Often it is not, and the extra memory just pushes the VPS closer to swapping without making any query faster. Before touching the config, it is worth confirming that the buffer pool really is too small for the data your site uses.
The InnoDB buffer pool is the area of memory where MySQL and MariaDB keep table and index pages they have already read from disk. When a query needs a row, InnoDB looks for the page in the pool first. If it is there, the read comes from RAM. If not, InnoDB reads the page from disk, places it in the pool, and may evict an older page to make room. An undersized pool produces a specific pattern. The part of your data that queries touch regularly, usually called the working set, does not fit in memory. Pages get evicted, requested again soon after, and read from disk again. Disk reads stay high even when traffic is steady, and they keep happening long after the server has warmed up. The working set is not the same as the whole database. A store with years of old orders can have a large database but a fairly small set of hot tables and indexes. That difference matters when you compare sizes later. InnoDB exposes two counters that describe the pattern directly. Innodb_buffer_pool_read_requests counts logical reads, meaning every time InnoDB asked for a page. Innodb_buffer_pool_reads counts the requests that could not be served from the pool and had to go to disk. The ratio between the two is your miss rate, and it is the core of this whole check.
These queries are read-only and safe to run on a live server:
SQL
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_wait_free';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages%';
SHOW GLOBAL STATUS LIKE 'Uptime';
SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_size';
The first pattern also returns read-ahead counters. You can ignore those for this check. The trap is that status counters are cumulative since the server started. Dividing the two totals gives you an average over the entire uptime, which includes the cold period right after the last restart, nightly backups, and any one-off import. A server restarted an hour ago will look terrible. A server that has run for months can hide a recent problem behind a large, healthy history. Sampling the change over time gives a much clearer picture. mysqladmin can print the difference between snapshots for you:
BASH
# Print counter deltas every 60 seconds. The first block shows absolute totals, so skip it. Ctrl+C to stop.
mysqladmin -r -i 60 extended-status | grep -E 'Innodb_buffer_pool_(reads|read_requests|wait_free) '
Depending on your setup you may need to pass -u and -p, or rely on a client config file that already holds credentials. Run it during a period when the site is actually slow, not at 4 AM when nobody is visiting. Now look at the deltas. As an illustrative example, if one minute shows 1,000,000 read requests and 100 disk reads, the pool is serving nearly everything from memory. If the same minute shows 1,000,000 requests and 50,000 disk reads, about 5% of page lookups are going to disk, and that is worth investigating. A common rule of thumb is that a warmed-up server with steady traffic typically sits well under 1%, but the acceptable level depends on your workload and how fast the storage is. The absolute number matters too, not just the ratio. With the default 16 KB page size, 50,000 page reads per minute is roughly 800 MB of reads per minute. On fast NVMe that may be tolerable; on slower or shared storage it will hurt. If you want background on how storage latency changes this, see NVMe SSD vs SATA in Web Hosting. Innodb_buffer_pool_wait_free is the other counter to watch. It increases when InnoDB needs a free page and has to wait for one to be flushed. On a healthy server it usually stays at or near zero. If it climbs during normal traffic, the pool is under real pressure.
The miss rate tells you that pages are being re-read. Data size tells you whether more memory could realistically stop that. This query estimates how much InnoDB data and index exists:
SQL
SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024) AS innodb_data_mb
FROM information_schema.TABLES
WHERE engine = 'InnoDB';
Two cautions. The sizes are estimates, not exact byte counts. And on servers with a lot of databases, which is common on shared hosting and busy cPanel boxes, this query can take a while and add load of its own, so run it off-peak. Compare the result with innodb_buffer_pool_size, which is reported in bytes. Then look at Innodb_buffer_pool_pages_free from the earlier output. If the pool is larger than your total InnoDB data and there are still plenty of free pages, size is not your problem. Everything fits, and any disk reads are coming from somewhere else or from a pool that is still warming up. If free pages are near zero, the miss rate is sustained, and your data is clearly larger than the pool, you are looking at a genuine fit problem. Even then, remember the working set point. A 20 GB database where only a few GB is hot may run fine on a modest pool, so the miss rate is a better guide than the raw size comparison.
This is where most wrong diagnoses happen. In support, the first questions are usually how long MySQL has been up and whether the slow query log points to one bad query, because those two explain a large share of requests for a bigger buffer pool. Warm-up after a restart is the simplest one. Right after MySQL starts, the pool is empty and every first read goes to disk. Unless your server is configured to reload a saved list of pages at startup, it can take a while under real traffic to settle. Sample again later before deciding anything. Missing indexes are the most common. A query that scans an entire large table inflates Innodb_buffer_pool_read_requests enormously, and if the table does not fit, it drags cold pages through the pool and pushes hot ones out. A bigger pool can hide this for a while, but the real fix is the query or the index. The slow query log and EXPLAIN will show it. Temporary tables written to disk create disk I/O that has nothing to do with the buffer pool. Compare Created_tmp_disk_tables with Created_tmp_tables in SHOW GLOBAL STATUS. If a large share of temporary tables land on disk, that I/O will look like a database read problem on your graphs. Swap is the trap that works in the opposite direction. If the pool is already too large for the VPS, the kernel starts swapping MySQL memory out, and the disk activity you see is swap traffic. Check free -m and the si and so columns in vmstat 5. If swap is active, increasing the pool makes things worse. The article on why 80% of RAM is not safe on a VPS that also runs the web stack covers that budgeting. Other processes can also be the reader. Backup jobs, mysqldump, malware scans, and log processing all read heavily from disk. Tools like iotop or pidstat -d, if installed, show which process is actually doing the I/O. If it is not mysqld or mariadbd, the buffer pool is not involved.
You can call the buffer pool the bottleneck with reasonable confidence when all of these hold at the same time: MySQL has been up long enough to warm up, the sampled miss rate stays high during normal traffic, free pages are near zero, Innodb_buffer_pool_wait_free is rising or disk reads are clearly coming from the database process, the hot data is larger than the pool, and the server is not swapping. If that is the case, increase the pool in a moderate step that leaves room for PHP-FPM, the web server, and per-connection MySQL buffers. Recent MySQL and MariaDB versions can resize the pool online with SET GLOBAL innodb_buffer_pool_size, but that change is lost at restart unless you also set it in the config file. Where that file lives depends on the distro and control panel. After the change, confirm the value is actually in effect with SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_size', since editing a config file alone does not prove the server loaded it. Then let the pool warm up and run the same mysqladmin sampling during a similar traffic period. You are looking for fewer disk reads per minute, a lower miss rate, and no new swap activity. If the miss rate dropped but the site is still slow, the buffer pool was only part of the story, and the next place to look is the queries themselves. The short version: do not resize on a hunch or on a single snapshot of lifetime counters. Sample the deltas after warm-up, check free pages and data size, rule out full scans, disk temp tables, swap, and other I/O, and only then add memory. Then measure again, because the second measurement is what tells you the change actually worked.