44
55### Why Asynchronous IO
66
7- Until the introduction of asynchronous IO postgres relied on the operating
8- system to hide the cost of synchronous IO from postgres. While this worked
9- surprisingly well in a lot of workloads, it does not do as good a job on
10- prefetching and controlled writeback as we would like.
11-
12- There are important expensive operations like ` fdatasync() ` where the operating
13- system cannot hide the storage latency. This is particularly important for WAL
14- writes, where the ability to asynchronously issue ` fdatasync() ` or O_DSYNC
15- writes can yield significantly higher throughput.
16-
7+ Postgres depends on IO operations happening asynchronously for reasonable
8+ performance: for instance, a sequential scan would be far slower without the
9+ benefit of readahead. Historically, Postgres only used synchronous APIs for
10+ IO, while assuming that the operating system would use the kernel buffer cache
11+ to make those operations asynchronous in most cases (aside from, e.g.,
12+ ` fdatasync() ` ).
13+
14+ The asynchronous IO APIs described here do not depend on that
15+ assumption. Instead, they allow different low-level IO methods, which are
16+ given more control and therefore rely less on the kernel's
17+ behavior. Currently, only async read operations are supported, but the
18+ infrastructure is designed to support async write operations in the future.
19+
20+ AIO is a practical prerequisite for Direct IO, which enables many efficiencies
21+ (see below). But even without using direct IO, AIO offers benefits: the kernel
22+ only performs readahead into its buffer cache; whereas an AIO worker is able
23+ to perform readahead directly into Postgres shared buffers. That means that a
24+ sequential scan doesn't need to wait for synchronous memory copies from the
25+ kernel buffers to Postgres shared buffers for each new block. (Without direct
26+ IO, the memory copy still needs to happen, but it can happen ahead of time in
27+ the AIO worker process.)
1728
1829### Why Direct / unbuffered IO
1930
2031The main reasons to want to use Direct IO are:
2132
22- - Lower CPU usage / higher throughput. Particularly on modern storage buffered
23- writes are bottlenecked by the operating system having to copy data from the
24- kernel's page cache to postgres buffer pool using the CPU. Whereas direct IO
25- can often move the data directly between the storage devices and postgres'
26- buffer cache, using DMA. While that transfer is ongoing, the CPU is free to
27- perform other work.
33+ - Avoid extra memory copies between the kernel buffer cache and Postgres
34+ shared buffers. These memory copies can become the bottleneck when the
35+ underlying storage has high enough throughput, which is common for
36+ solid-state drives or fast network block devices. Instead, direct IO can
37+ often move the data directly between the Postgres buffer cache and the
38+ device by using DMA, leaving the CPU free to perform other work.
2839- Reduced latency - Direct IO can have substantially lower latency than
2940 buffered IO, which can be impactful for OLTP workloads bottlenecked by WAL
3041 write latency.
@@ -37,11 +48,24 @@ The main reasons *not* to use Direct IO are:
3748
3849- Without AIO, Direct IO is unusably slow for most purposes.
3950- Even with AIO, many parts of postgres need to be modified to perform
40- explicit prefetching.
51+ explicit prefetching (see read_stream.c) .
4152- In situations where shared_buffers cannot be set appropriately large,
4253 e.g. because there are many different postgres instances hosted on shared
4354 hardware, performance will often be worse than when using buffered IO.
4455
56+ ### Writing WAL
57+
58+ Using AIO and Direct IO can reduce the overhead of WAL logging
59+ substantially:
60+
61+ - AIO allows to start WAL writes eagerly, so they complete before needing to
62+ wait
63+ - AIO allows to have multiple WAL flushes in progress at the same time
64+ - Direct IO can reduce the number of roundtrips to storage on some OSs
65+ and storage HW (buffered IO and direct IO without O_DSYNC needs to
66+ issue a write and after the write's completion a cache flush,
67+ whereas O\_ DIRECT + O\_ DSYNC can use a single Force Unit Access
68+ (FUA) write).
4569
4670## AIO Usage Example
4771
@@ -196,25 +220,15 @@ processing to the AIO workers).
196220
197221### IO can be started in critical sections
198222
199- Using AIO for WAL writes can reduce the overhead of WAL logging substantially:
200223
201- - AIO allows to start WAL writes eagerly, so they complete before needing to
202- wait
203- - AIO allows to have multiple WAL flushes in progress at the same time
204- - AIO makes it more realistic to use O\_DIRECT + O\_DSYNC, which can reduce
205- the number of roundtrips to storage on some OSs and storage HW (buffered IO
206- and direct IO without O_DSYNC needs to issue a write and after the write's
207- completion a cache flush, whereas O\_DIRECT + O\_DSYNC can use a single
208- Force Unit Access (FUA) write).
209-
210- The need to be able to execute IO in critical sections has substantial design
211- implication on the AIO subsystem. Mainly because completing IOs (see prior
212- section) needs to be possible within a critical section, even if the
213- to-be-completed IO itself was not issued in a critical section. Consider
214- e.g. the case of a backend first starting a number of writes from shared
215- buffers and then starting to flush the WAL. Because only a limited amount of
216- IO can be in-progress at the same time, initiating IO for flushing the WAL may
217- require to first complete IO that was started earlier.
224+ To be able to use AIO for WAL, it must be possible to use inside a critical
225+ section, which has substantial design implications. Mainly because completing
226+ IOs (see prior section) needs to be possible within a critical section, even
227+ if the to-be-completed IO itself was not issued in a critical
228+ section. Consider e.g. the case of a backend first starting a number of writes
229+ from shared buffers and then starting to flush the WAL. Because only a limited
230+ amount of IO can be in-progress at the same time, initiating IO for flushing
231+ the WAL may require to first complete IO that was started earlier.
218232
219233
220234### State for AIO needs to live in shared memory
0 commit comments