Skip to content
dbexplore

Glossary: Statistics views

Statistics reset, and the rates it breaks

Also called: stats_reset, pg_stat_reset.

Definition, revised in place. Last updated .

A statistics reset sets PostgreSQL’s cumulative activity counters back to zero and records when it happened. Almost everything in the pg_stat_ views is a counter that only ever increases, and every rate an operator reads is really a difference between two samples of one. Resetting breaks that arithmetic for exactly one interval: the later sample is smaller than the earlier one, so the computed rate is negative, and what the graph does next depends entirely on how the collector handles that.

One interval of nonsense, and where it comes from

Most collectors treat a counter that went backwards as a restart and either drop the interval or, worse, report the new absolute value as though it were the delta. The first leaves a gap nobody investigates. The second produces a spike that looks like a genuine burst of work, arriving at a moment when somebody was already touching the database, which is the most misleading combination available.

Resets are rarely deliberate. A server restart clears some of these counters and not others. Running pg_stat_reset() to get a clean reading during an investigation zeroes the whole database’s activity for every other consumer of those numbers at the same time. pg_stat_statements_reset() is routine in tooling that wants a clean window and takes the fingerprint history with it. And the shared counters have their own function with a well-known trap: calling it without an argument on PostgreSQL 17 resets more than the caller usually intends.

Because of that, the stats_reset timestamp is not a curiosity, it is the field that makes every other field interpretable. A rate calculation that does not read it is computing an average over an unknown window.

Watching a counter go backwards

The effect takes three statements to reproduce.

SELECT xact_commit, blks_hit FROM pg_stat_database WHERE datname = current_database();
SELECT pg_stat_reset();
SELECT pg_sleep(1);
SELECT xact_commit, blks_hit FROM pg_stat_database WHERE datname = current_database();
 xact_commit | blks_hit 
-------------+----------
           1 |     1300
(1 row)

 pg_stat_reset 
---------------
 
(1 row)

 pg_sleep 
----------
 
(1 row)

 xact_commit | blks_hit 
-------------+----------
           3 |      167
(1 row)

blks_hit has fallen from 1300 to 167 without the server doing less work, and a collector sampling either side of that reset has just recorded a large negative rate. The capture is from 14.24, where the reset is applied by a separate collector process and the pg_sleep is what lets it land before the second read. From PostgreSQL 15 the counters live in shared memory and the change is visible at once, so the sleep is there for the oldest supported major and costs the others a second.

The other half of the demonstration is that xact_commit is now three rather than zero, because the two statements after the reset were themselves transactions. Resetting does not give you a quiet server, it gives you a new origin.

Building rates that survive it

Read stats_reset beside any counter you difference, and discard an interval whose timestamp moved. Which counters a given reset actually clears differs by view and by major, and the checkpointer’s counters are the clearest case, covered on the page for the checkpointer view in PostgreSQL 17. For what moved between views in the newer majors, and which panels that leaves reading zero, see what PostgreSQL 18 changed in monitoring.

Put every Postgres you run on autopilot.

We onboard teams in small batches. Tell us about your fleet and we will reach out when a seat opens. One email, no drip campaign.