How innodb_flush_method controls the interaction with the page cache
MySQL already caches database pages in the InnoDB buffer pool in RAM. If the Linux kernel simultaneously uses its own page cache for the same data, double buffering occurs: the same memory content sits twice in RAM without any extra benefit. O_DIRECT deliberately bypasses the page cache and hands full caching control to InnoDB, while buffered I/O remains the more pragmatic choice in certain scenarios.
Table of Contents
- 1. Why the I/O mode matters for InnoDB
- 2. Buffered I/O: the page cache as a second layer over InnoDB
- 3. O_DIRECT: deliberately avoiding double buffering
- 4. innodb_flush_method in detail: fsync, O_DIRECT, O_DIRECT_NO_FSYNC
- 5. When O_DIRECT is the better choice
- 6. When buffered I/O still makes sense
- 7. Configuration and validation on Linux
- 8. Pitfalls: alignment, filesystem, and false expectations
- 9. O_DIRECT and buffered I/O side by side
- 10. Summary
- 11. FAQ
1. Why the I/O mode matters for InnoDB
Anyone working on MySQL performance on Linux servers sooner or later runs into a configuration line that looks unremarkable at first glance but has far reaching consequences for the memory usage of the entire server: innodb_flush_method. This setting decides whether MySQL competes with the Linux kernel for memory, or whether the kernel leaves memory management entirely to the database.
Every read and write operation MySQL performs on the InnoDB storage engine's data files runs through one of two fundamental Linux kernel I/O modes: buffered I/O, where the kernel additionally keeps every page in its own page cache, or O_DIRECT, where data is transferred directly between MySQL's application memory and the block device without passing through the page cache. This decision is made via the innodb_flush_method parameter in the MySQL configuration and directly affects how efficiently a database server's available RAM is used.
For a Magento installation with a dedicated database server and a generously sized InnoDB buffer pool, this decision is not a minor detail. A 32 GB buffer pool on a server with 64 GB of RAM is meant to keep frequently read database pages fully in memory. If the kernel simultaneously uses buffered I/O, it keeps the same pages a second time in the page cache, leaving effectively less free memory available for the actual purpose than the configuration would suggest.
The effect of this double caching often only becomes visible once memory gets tight under load and the kernel starts dropping page cache pages under pressure or, worse, pushing them into swap. O_DIRECT eliminates this problem at the root by removing database pages from the page cache entirely and leaving full caching responsibility to the InnoDB buffer pool, which was designed for exactly this purpose in the first place.
2. Buffered I/O: the page cache as a second layer over InnoDB
To put this decision into perspective, it helps to first look at the general role of the page cache in the Linux kernel before considering the MySQL specific implications.
Without explicit configuration, the Linux kernel uses buffered I/O by default for all file access. Every page read from a data file ends up in the page cache, even if the application requesting the data already has its own specialized cache. For generic applications this behavior makes sense, because the kernel cannot know which applications implement their own caching strategies. For MySQL/InnoDB with a generously sized buffer pool, however, it leads to a structural inefficiency.
The core problem is double buffering: the same 16 KB InnoDB page sits once in the InnoDB buffer pool and a second time in the Linux page cache in RAM, without the second cache providing any additional benefit to the database. InnoDB already manages its own cache with an LRU algorithm tailored to database access patterns, distinguishing between freshly read and repeatedly used pages. The kernel's page cache does not know this distinction and manages pages purely by general access frequency.
In practice, buffered I/O also adds extra CPU and memory copy overhead for MySQL: data has to be copied from the block device into the page cache and from there once more into the InnoDB buffer pool, instead of going directly into the target memory. With write heavy workloads, such as during a Magento reindex run with many INSERT and UPDATE operations, this double copy overhead adds up to a measurable extra CPU load.
# my.cnf — default behavior without explicit flush method
# On many distributions this historically defaulted to fsync,
# which still uses the page cache for reads (buffered I/O)
[mysqld]
innodb_flush_method = fsync
innodb_buffer_pool_size = 32G
3. O_DIRECT: deliberately avoiding double buffering
Understanding this flag at the system call level makes it easier to reason about its effect on a running MySQL instance, rather than treating it as an opaque configuration toggle.
O_DIRECT is a flag that can be passed when opening a file, instructing the kernel to bypass the page cache for that file. Data is then transferred directly between the application's user memory, in this case the InnoDB buffer pool, and the block device. MySQL uses O_DIRECT when innodb_flush_method is set accordingly, taking on full responsibility for caching database pages without support from the kernel page cache.
The immediate advantage of O_DIRECT: the entire memory reserved for the buffer pool is actually available for database pages, without being diluted by a redundant second cache. On a server with 64 GB of RAM and a 48 GB buffer pool, O_DIRECT actually leaves more room for the operating system and other processes, while with buffered I/O the same memory is partly used twice for the same data.
Another effect of O_DIRECT: write operations become more synchronous and predictable, because they are not first staged in the page cache and asynchronously written to disk by the kernel's writeback mechanism. For InnoDB, which already brings its own redo log and its own checkpoint logic for consistency and crash recovery, this more direct control over the timing of physical writes is generally advantageous, since InnoDB itself can better estimate when a flush is actually necessary.
4. innodb_flush_method in detail: fsync, O_DIRECT, O_DIRECT_NO_FSYNC
MySQL offers several values via innodb_flush_method that control the interaction with the Linux I/O stack differently. fsync is the classic default: read and write access runs through buffered I/O, and InnoDB calls fsync() to ensure data has actually been written to disk rather than sitting only in the page cache. This mode is the safest compromise for generic setups, but not the most efficient for dedicated database servers.
O_DIRECT enables direct I/O mode for data files while still using fsync() calls to confirm writes. This is the most commonly recommended value for dedicated MySQL servers with a sufficiently large buffer pool. O_DIRECT_NO_FSYNC, available since MySQL 8.0, goes one step further and skips redundant fsync() calls on filesystems that already guarantee consistency for O_DIRECT writes, such as XFS under certain conditions, allowing additional overhead reduction but requiring careful testing before production use.
For read replicas primarily used for read load in a Magento setup with separate read and write instances, a similar consideration applies: as long as the buffer pool there is also sized large enough to fully hold the frequently read catalog and price data, O_DIRECT brings the same benefit as on the write instance. Only with significantly smaller replica instances and a tighter buffer pool does this benefit diminish again.
# my.cnf — recommended setting for a dedicated MySQL server
[mysqld]
innodb_flush_method = O_DIRECT
innodb_buffer_pool_size = 48G
innodb_buffer_pool_instances = 8
innodb_flush_log_at_trx_commit = 1
5. When O_DIRECT is the better choice
The following criteria are meant as a practical checklist rather than a strict decision tree, since real Magento hosting setups rarely fit a single clean category.
As a rough rule of thumb: as soon as the InnoDB buffer pool occupies more than half of the physical RAM on a dedicated database server, the benefit of O_DIRECT over buffered I/O clearly outweighs the alternative in nearly all practical cases.
O_DIRECT shows its strengths mainly on dedicated database servers where a large share of the available RAM has deliberately been assigned to the InnoDB buffer pool. When the buffer pool already occupies 60 to 80 percent of physical memory, an additional page cache barely provides any benefit and instead competes directly for the same scarce memory. For Magento setups with a dedicated MySQL instance on its own hardware or its own VM, this is the standard case, not the exception.
Another criterion for O_DIRECT is write heavy load, as seen with frequent price and stock updates, large batch imports, or the nightly reindexing of typical Magento catalogs. Since O_DIRECT eliminates the extra copy step through the page cache, CPU load per write operation drops, which shows up especially in shorter run times for I/O bound batch jobs. On storage using NVMe SSDs, which already deliver very low latencies, the speed advantage of the page cache for repeated reads is also smaller than with classic hard drives, making O_DIRECT even more attractive.
6. When buffered I/O still makes sense
None of the exceptions below invalidate O_DIRECT as a general recommendation, they simply mark the boundary where the trade off tips the other way.
This counter check matters so that switching to O_DIRECT does not become a reflexive default measure without examining the specific use case.
Despite the advantages of O_DIRECT, there are scenarios where buffered I/O remains the more pragmatic choice. For small MySQL instances with a buffer pool of only a few gigabytes on a server with significantly more free RAM, the page cache can actually catch additional reads, for example for files outside the direct InnoDB dataset or for mixed workloads where the same server also serves other services.
Certain network filesystems such as NFS also do not always implement O_DIRECT reliably, or the respective NFS server does not fully support it, which can lead to inconsistent behavior. In these cases, buffered I/O is the safer, if less efficient, option. Likewise, for very small test environments, the configuration and validation effort for O_DIRECT can be out of proportion to the actual performance gain when enough RAM is already available for both caching layers anyway.
7. Configuration and validation on Linux
These practical steps apply regardless of whether the server is already in production or is being newly set up.
Switching to O_DIRECT is done through the MySQL configuration file and requires restarting the database service, since innodb_flush_method cannot be changed at runtime. Before restarting, it should be confirmed that the filesystem in use supports O_DIRECT, which is the default case with ext4 and XFS on modern Linux kernels. After the restart, the actually used flag can be verified with strace on a running mysqld process.
To validate the effect, a comparison of memory usage with free -h before and after the switch is useful, along with a direct benchmark using sysbench under realistic load. A sensible test measures both pure read access and mixed read write workloads, since the effect of O_DIRECT varies depending on the access pattern.
Before enabling the change in production, it is also worth taking a quick look at the MySQL error log after the restart. Some combinations of kernel version, filesystem, and storage driver do not report missing O_DIRECT support as a hard error but silently fall back to buffered I/O, which can only be uncovered through the logs or a later strace check.
In addition to pure functional validation, it is worth watching iostat -x 1 during the benchmark to see whether the average queue depth (avgqu-sz) and per request latency actually change after the switch. If these values remain almost unchanged, that suggests the limiting factor is not the I/O mode but something else, such as storage latency itself.
# Verify which I/O flag mysqld actually uses after restart
sudo strace -f -e trace=open,openat -p "$(pgrep -x mysqld)" 2>&1 | grep -i direct
# Compare page cache usage before and after switching to O_DIRECT
free -h
# 'buff/cache' should shrink noticeably for the InnoDB data files
# once double buffering through the page cache is eliminated
# Run a mixed read/write benchmark to measure the practical effect
sysbench oltp_read_write --table-size=1000000 --threads=8 \
--mysql-db=magento --time=120 run
8. Pitfalls: alignment, filesystem, and false expectations
Most of these pitfalls only surface under real production conditions, which is exactly why a staging rollout with representative data volumes matters more than a quick local test.
An often underestimated point is handling tablespace files on compressed or deduplicated filesystems, such as Btrfs with compression enabled. O_DIRECT and transparent filesystem compression do not always coexist smoothly, since compression introduces extra processing steps that contradict the concept of a direct, unaltered data transfer. For production MySQL servers, an uncompressed ext4 or XFS filesystem is generally recommended for the InnoDB data files.
A common mistake when switching to O_DIRECT: expecting a dramatic performance jump that rarely materializes in this form. O_DIRECT mainly optimizes memory usage and reduces CPU overhead by avoiding double buffering, but it is not a substitute for a sufficiently sized buffer pool or fast storage. Anyone working with too small a buffer pool or slow disks will not see a fundamental improvement even with O_DIRECT, because the actual bottleneck remains unchanged.
A more technical issue concerns alignment: O_DIRECT operations require buffers, offsets, and transfer sizes to be aligned to the filesystem's block sizes, usually 512 bytes or 4 KB. InnoDB already handles this alignment internally for its 16 KB pages, but with unusual storage configurations, such as certain network storage solutions with a different block size, alignment issues can cause file access errors. Before production use, O_DIRECT should therefore always be tested first in a staging environment with an identical storage configuration.
A third, often overlooked point: backup tools and snapshot mechanisms that access the data files themselves, such as physical backups with xtrabackup, operate independently of MySQL's innodb_flush_method setting and can continue running through buffered I/O. This is not a bug, but should be considered in server capacity planning, since a backup run temporarily requires additional page cache memory regardless of MySQL's own configuration.
9. O_DIRECT and buffered I/O side by side
A concise side by side view like this is often more actionable during a real migration decision than lengthy prose alone.
The following table summarizes the key differences between both I/O modes for typical MySQL setups in a Magento hosting context.
| Criterion | Buffered I/O (fsync) | O_DIRECT | Recommendation |
|---|---|---|---|
| Memory usage | Double buffering possible | Buffer pool fully usable | O_DIRECT with large pool |
| CPU overhead under write load | Extra copy steps | Direct transfer | O_DIRECT for batch jobs |
| Small instances with plenty of free RAM | Extra cache benefit | Barely any difference | Buffered I/O sufficient |
| NFS or network storage | Reliably supported | Not always dependable | Verify buffered I/O |
| Configuration effort | Default, no effort | Restart and validation needed | Test in staging first |
The table shows: O_DIRECT is almost always the better choice for dedicated, generously sized production databases, while buffered I/O still has its place for small instances, mixed workloads, or certain network storage scenarios.
For the concrete decision in your own setup, a brief inventory is worthwhile: how large is the buffer pool relative to total RAM, how write heavy is the daily load from Magento batch jobs, and which filesystem is used for the data files. Only once these three questions are answered can it be seriously estimated whether O_DIRECT delivers the expected effect or whether buffered I/O remains the more pragmatic default in the specific case.
Mironsoft
MySQL performance tuning for Magento database servers
Buffer pool and page cache fighting over the same memory?
We analyze your InnoDB configuration, determine the right innodb_flush_method for your storage environment, and validate the effect with realistic benchmarks before the change goes to production.
InnoDB audit
Review buffer pool, flush method, and memory usage in detail
Benchmark comparison
Test O_DIRECT against buffered I/O with sysbench under real load
Staging validation
Check alignment and filesystem compatibility before the production rollout
10. Summary
The choice between O_DIRECT and buffered I/O on Linux significantly determines how efficiently a database server uses its RAM for the InnoDB buffer pool. Buffered I/O leads to double buffering with generously sized buffer pools, because the same database pages are additionally kept in the Linux page cache without providing any real extra benefit. O_DIRECT avoids this problem by completely skipping the page cache for data files and leaving sole caching responsibility to InnoDB.
For dedicated Magento database servers with a generously configured buffer pool, O_DIRECT via innodb_flush_method is the better choice in most cases, especially under write heavy load from batch imports or reindexing. Small instances, mixed workloads, or certain network storage scenarios, on the other hand, can still benefit from buffered I/O. The switch should always be tested in a staging environment and validated with realistic benchmarks rather than relying solely on theoretical expectations.
For teams maintaining an existing Magento infrastructure, this review is worth repeating regularly, not just once during the initial server setup. If the buffer pool is enlarged as part of a RAM upgrade, the ratio between buffer pool and remaining page cache changes too, which can justify reassessing O_DIRECT against buffered I/O.
O_DIRECT vs. Buffered I/O for MySQL — The Key Facts at a Glance
Core problem
Buffered I/O keeps database pages twice: in the InnoDB buffer pool and in the Linux page cache.
Solution
innodb_flush_method = O_DIRECT bypasses the page cache and gives InnoDB full cache control.
When O_DIRECT
Dedicated servers with a large buffer pool and write heavy batch load, such as reindexing.
Validation
Test with strace, free -h, and sysbench before and after the switch in staging.