DB Internals AtlasPostgreSQL 18.6 Home About

Parameter · Statistics / Cumulative Query and Index Statistics

track_io_timing

Collects timing statistics for database I/O activity.

  • Default: off
  • Type: bool
  • Applies: Session (superuser)

In plain words

Counting how many blocks were read is useful, but knowing how long those reads took tells you whether storage is the bottleneck. With this on, PostgreSQL times every block read and write and adds the totals to its statistics and to EXPLAIN (ANALYZE, BUFFERS).

It reads the system clock very often; on most modern Linux servers that is cheap, and many DBAs enable it everywhere.

For DBAs and operators

Where the times appear (PostgreSQL 18):

  • pg_stat_database.blk_read_time / blk_write_time (ms)
  • pg_stat_io read_time, write_time, writeback_time, extend_time, fsync_time for every object except wal (WAL rows need track_wal_io_timing), and the per-backend pg_stat_get_backend_io(pid)
  • EXPLAIN (ANALYZE, BUFFERS): I/O Timings: shared read=12.345 write=0.512 under each node (only when time was spent)
  • pg_stat_statements shared_blk_read_time, shared_blk_write_time, local_blk_*, temp_blk_*
  • VACUUM VERBOSE and autovacuum logs (I/O timings: read: … ms, write: … ms)

Before enabling, run pg_test_timing: the docs call it good when more than 90% of timing calls take under 1 µs and the average loop overhead is below 100 ns (typical with the TSC clock source); then the overhead is negligible. Slower clock sources such as acpi_pm (seen on some virtual machines) make it noticeably more expensive. Without it, the read counts are still there but all time columns stay 0, which is a frequent confusion in support tickets ("blk_read_time is zero, so I/O is not the problem").

Note that a "read" here is a request to the OS; it may be served from the OS page cache, so fast reads do not prove the data was in shared buffers. In 18 with asynchronous I/O, read time is the time the backend spent issuing the read plus the time it then waited for it to complete; I/O that finished in the background before the backend needed it adds no wait time.

How to change it

A superuser (or a role granted SET) can change it per session with SET.

See it in context

Cumulative statistics Shared buffers Asynchronous I/O Life of a SELECT

Related parameters

track_wal_io_timing track_counts track_cost_delay_timing io_method

All Statistics parameters