Direct I/O (DIO) refers to a method of reading and writing data directly between an application’s buffers and the storage device, bypassing the Operating System’s (OS) file system cache (also known as the page cache).
While historically PostgreSQL has relied on buffered I/O, using the OS cache, support for Direct I/O (often integrated with Asynchronous I/O or AIO) has been an ongoing area of development and improvement to address certain performance and architectural challenges.
What is Direct I/O?
In a standard (buffered) I/O operation, when a PostgreSQL backend process needs to read a block of data not present in its own shared_buffers, it asks the OS to read the file. The OS first checks its page cache. If the data is not there, the OS reads it from the physical disk into its page cache, and then copies it to the PostgreSQL process’s buffer.
Direct I/O changes this by instructing the OS to bypass its page cache entirely. The data is transferred directly between the disk and PostgreSQL’s own shared_buffers or a dedicated user buffer, usually via Direct Memory Access (DMA).
How Direct I/O Works in PostgreSQL
PostgreSQL’s traditional architecture involves double buffering: data is cached once in the database’s shared_buffers and potentially again in the OS page cache. DIO aims to eliminate the OS page cache layer for database files, giving PostgreSQL more explicit control over its I/O.
Key aspects of how DIO is implemented or considered in PostgreSQL:
Bypassing the OS Cache: When an application uses DIO (typically via flags like
O_DIRECTon Linux), the read/write call goes straight to the storage device, avoiding the OS cache layer. This eliminates the CPU overhead and memory usage associated with copying data between the two caches.Integration with Asynchronous I/O (AIO): For Direct I/O to be efficient, it must be paired with Asynchronous I/O (AIO). Since a synchronous DIO call would block the process until the I/O completes (which is very slow), AIO allows the database process to issue an I/O request and continue processing other tasks, only coming back to check for the I/O completion later. PostgreSQL has an AIO framework that can utilize various platform-specific AIO methods, sometimes falling back to a worker-process model for asynchronous behavior.
Explicit Data Management: Without the OS providing automatic readahead (prefetching) and managing dirty buffer writeback, PostgreSQL must implement these features itself to maintain performance. This is a significant architectural change. The development of features like ReadStream (for prefetching during sequential scans) is part of this effort.
Advantages of Direct I/O for PostgreSQL:
Avoids Double Buffering: Saves memory and CPU cycles by eliminating redundant caching in the OS page cache.
Lower CPU Usage: By using DMA more effectively, data transfer can happen with less CPU involvement, particularly for modern storage.
Better Control: Gives the database better control over when data is written to disk (writeback) and how data is read (prefetching), leading to more predictable performance.
Challenges and Current Status:
As of recent PostgreSQL versions (v17/v18 development), full-fledged, production-ready Direct I/O for all data access paths is a major, ongoing effort. Historically, full DIO was not the default due to concerns about portability across operating systems and the massive changes required to the core I/O logic.
Default Behavior: PostgreSQL currently uses buffered I/O by default, relying on the OS cache for performance.
Experimental/Debug Use: PostgreSQL does have developer/debugging settings (
debug_io_direct) that allow turning on DIO for certain data types (e.g., relation data or WAL) to test the performance benefits, but these are not intended for production use as they may severely degrade performance on most workloads without other required AIO optimizations.
Example (Configuration)
While full production use of Direct I/O is not yet the default or fully optimized, the ongoing development in PostgreSQL often uses configuration settings (typically for debugging or testing) to isolate its impact.
For example, to test the effect of Direct I/O on relation data (using a GUC that is often only available in recent/development versions and meant for testing):
-- This is a developer/testing GUC and should NOT be used in production.
ALTER SYSTEM SET debug_io_direct = 'data';
SELECT pg_reload_conf();
Note: The performance impact of just enabling this flag without the corresponding AIO and prefetching logic being fully implemented across all parts of the system can be extremely negative. The final, stable implementation will likely not use a debug flag but a standard configuration parameter once it is deemed robust and performant.