Skip to content

Commit ae4fdde

Browse files
Count updates that move row to a new page.
Add pgstat counter to track row updates that result in the successor version going to a new heap page, leaving behind an original version whose t_ctid points to the new version. The current count is shown by the n_tup_newpage_upd column of each of the pg_stat_*_tables views. The new n_tup_newpage_upd column complements the existing n_tup_hot_upd and n_tup_upd columns. Tables that have high n_tup_newpage_upd values (relative to n_tup_upd) are good candidates for tuning heap fillfactor. Corey Huinker, with small tweaks by me. Author: Corey Huinker <corey.huinker@gmail.com> Reviewed-By: Peter Geoghegan <pg@bowt.ie> Reviewed-By: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/CADkLM=ded21M9iZ36hHm-vj2rE2d=zcKpUQMds__Xm2pxLfHKA@mail.gmail.com
1 parent 3b50275 commit ae4fdde

File tree

9 files changed

+80
-16
lines changed

9 files changed

+80
-16
lines changed

doc/src/sgml/monitoring.sgml

+23-5
Original file line numberDiff line numberDiff line change
@@ -4789,7 +4789,7 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
47894789
<structfield>n_tup_ins</structfield> <type>bigint</type>
47904790
</para>
47914791
<para>
4792-
Number of rows inserted
4792+
Total number of rows inserted
47934793
</para></entry>
47944794
</row>
47954795

@@ -4798,7 +4798,10 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
47984798
<structfield>n_tup_upd</structfield> <type>bigint</type>
47994799
</para>
48004800
<para>
4801-
Number of rows updated (includes <link linkend="storage-hot">HOT updated rows</link>)
4801+
Total number of rows updated. (This includes row updates
4802+
counted in <structfield>n_tup_hot_upd</structfield> and
4803+
<structfield>n_tup_newpage_upd</structfield>, and remaining
4804+
non-<acronym>HOT</acronym> updates.)
48024805
</para></entry>
48034806
</row>
48044807

@@ -4807,7 +4810,7 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
48074810
<structfield>n_tup_del</structfield> <type>bigint</type>
48084811
</para>
48094812
<para>
4810-
Number of rows deleted
4813+
Total number of rows deleted
48114814
</para></entry>
48124815
</row>
48134816

@@ -4816,8 +4819,23 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
48164819
<structfield>n_tup_hot_upd</structfield> <type>bigint</type>
48174820
</para>
48184821
<para>
4819-
Number of rows HOT updated (i.e., with no separate index
4820-
update required)
4822+
Number of rows <link linkend="storage-hot">HOT updated</link>.
4823+
These are updates where no successor versions are required in
4824+
indexes.
4825+
</para></entry>
4826+
</row>
4827+
4828+
<row>
4829+
<entry role="catalog_table_entry"><para role="column_definition">
4830+
<structfield>n_tup_newpage_upd</structfield> <type>bigint</type>
4831+
</para>
4832+
<para>
4833+
Number of rows updated where the successor version goes onto a
4834+
<emphasis>new</emphasis> heap page, leaving behind an original
4835+
version with a
4836+
<link linkend="storage-tuple-layout"><structfield>t_ctid</structfield>
4837+
field</link> that points to a different heap page. These are
4838+
always non-<acronym>HOT</acronym> updates.
48214839
</para></entry>
48224840
</row>
48234841

src/backend/access/heap/heapam.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -3803,7 +3803,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
38033803
if (have_tuple_lock)
38043804
UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode);
38053805

3806-
pgstat_count_heap_update(relation, use_hot_update);
3806+
pgstat_count_heap_update(relation, use_hot_update, newbuf != buffer);
38073807

38083808
/*
38093809
* If heaptup is a private copy, release it. Don't forget to copy t_self

src/backend/catalog/system_views.sql

+3-1
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ CREATE VIEW pg_stat_all_tables AS
665665
pg_stat_get_tuples_updated(C.oid) AS n_tup_upd,
666666
pg_stat_get_tuples_deleted(C.oid) AS n_tup_del,
667667
pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
668+
pg_stat_get_tuples_newpage_updated(C.oid) AS n_tup_newpage_upd,
668669
pg_stat_get_live_tuples(C.oid) AS n_live_tup,
669670
pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
670671
pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
@@ -696,7 +697,8 @@ CREATE VIEW pg_stat_xact_all_tables AS
696697
pg_stat_get_xact_tuples_inserted(C.oid) AS n_tup_ins,
697698
pg_stat_get_xact_tuples_updated(C.oid) AS n_tup_upd,
698699
pg_stat_get_xact_tuples_deleted(C.oid) AS n_tup_del,
699-
pg_stat_get_xact_tuples_hot_updated(C.oid) AS n_tup_hot_upd
700+
pg_stat_get_xact_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
701+
pg_stat_get_xact_tuples_newpage_updated(C.oid) AS n_tup_newpage_upd
700702
FROM pg_class C LEFT JOIN
701703
pg_index I ON C.oid = I.indrelid
702704
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)

src/backend/utils/activity/pgstat_relation.c

+10-2
Original file line numberDiff line numberDiff line change
@@ -373,18 +373,25 @@ pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
373373
* count a tuple update
374374
*/
375375
void
376-
pgstat_count_heap_update(Relation rel, bool hot)
376+
pgstat_count_heap_update(Relation rel, bool hot, bool newpage)
377377
{
378+
Assert(!(hot && newpage));
379+
378380
if (pgstat_should_count_relation(rel))
379381
{
380382
PgStat_TableStatus *pgstat_info = rel->pgstat_info;
381383

382384
ensure_tabstat_xact_level(pgstat_info);
383385
pgstat_info->trans->tuples_updated++;
384386

385-
/* t_tuples_hot_updated is nontransactional, so just advance it */
387+
/*
388+
* t_tuples_hot_updated and t_tuples_newpage_updated counters are
389+
* nontransactional, so just advance them
390+
*/
386391
if (hot)
387392
pgstat_info->t_counts.t_tuples_hot_updated++;
393+
else if (newpage)
394+
pgstat_info->t_counts.t_tuples_newpage_updated++;
388395
}
389396
}
390397

@@ -804,6 +811,7 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
804811
tabentry->tuples_updated += lstats->t_counts.t_tuples_updated;
805812
tabentry->tuples_deleted += lstats->t_counts.t_tuples_deleted;
806813
tabentry->tuples_hot_updated += lstats->t_counts.t_tuples_hot_updated;
814+
tabentry->tuples_newpage_updated += lstats->t_counts.t_tuples_newpage_updated;
807815

808816
/*
809817
* If table was truncated/dropped, first reset the live/dead counters.

src/backend/utils/adt/pgstatfuncs.c

+18
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ PG_STAT_GET_RELENTRY_INT64(tuples_fetched)
9292
/* pg_stat_get_tuples_hot_updated */
9393
PG_STAT_GET_RELENTRY_INT64(tuples_hot_updated)
9494

95+
/* pg_stat_get_tuples_newpage_updated */
96+
PG_STAT_GET_RELENTRY_INT64(tuples_newpage_updated)
97+
9598
/* pg_stat_get_tuples_inserted */
9699
PG_STAT_GET_RELENTRY_INT64(tuples_inserted)
97100

@@ -1618,6 +1621,21 @@ pg_stat_get_xact_tuples_hot_updated(PG_FUNCTION_ARGS)
16181621
PG_RETURN_INT64(result);
16191622
}
16201623

1624+
Datum
1625+
pg_stat_get_xact_tuples_newpage_updated(PG_FUNCTION_ARGS)
1626+
{
1627+
Oid relid = PG_GETARG_OID(0);
1628+
int64 result;
1629+
PgStat_TableStatus *tabentry;
1630+
1631+
if ((tabentry = find_tabstat_entry(relid)) == NULL)
1632+
result = 0;
1633+
else
1634+
result = (int64) (tabentry->t_counts.t_tuples_newpage_updated);
1635+
1636+
PG_RETURN_INT64(result);
1637+
}
1638+
16211639
Datum
16221640
pg_stat_get_xact_blocks_fetched(PG_FUNCTION_ARGS)
16231641
{

src/include/catalog/catversion.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@
5757
*/
5858

5959
/* yyyymmddN */
60-
#define CATALOG_VERSION_NO 202303181
60+
#define CATALOG_VERSION_NO 202303231
6161

6262
#endif

src/include/catalog/pg_proc.dat

+10
Original file line numberDiff line numberDiff line change
@@ -5360,6 +5360,11 @@
53605360
proname => 'pg_stat_get_tuples_hot_updated', provolatile => 's',
53615361
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
53625362
prosrc => 'pg_stat_get_tuples_hot_updated' },
5363+
{ oid => '8614',
5364+
descr => 'statistics: number of tuples updated onto a new page',
5365+
proname => 'pg_stat_get_tuples_newpage_updated', provolatile => 's',
5366+
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
5367+
prosrc => 'pg_stat_get_tuples_newpage_updated' },
53635368
{ oid => '2878', descr => 'statistics: number of live tuples',
53645369
proname => 'pg_stat_get_live_tuples', provolatile => 's', proparallel => 'r',
53655370
prorettype => 'int8', proargtypes => 'oid',
@@ -5823,6 +5828,11 @@
58235828
proname => 'pg_stat_get_xact_tuples_hot_updated', provolatile => 'v',
58245829
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
58255830
prosrc => 'pg_stat_get_xact_tuples_hot_updated' },
5831+
{ oid => '8615',
5832+
descr => 'statistics: number of tuples updated onto a new page in current transaction',
5833+
proname => 'pg_stat_get_xact_tuples_newpage_updated', provolatile => 'v',
5834+
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
5835+
prosrc => 'pg_stat_get_xact_tuples_newpage_updated' },
58265836
{ oid => '3044',
58275837
descr => 'statistics: number of blocks fetched in current transaction',
58285838
proname => 'pg_stat_get_xact_blocks_fetched', provolatile => 'v',

src/include/pgstat.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ typedef struct PgStat_BackendSubEntry
151151
* the index AM, while tuples_fetched is the number of tuples successfully
152152
* fetched by heap_fetch under the control of simple indexscans for this index.
153153
*
154-
* tuples_inserted/updated/deleted/hot_updated count attempted actions,
155-
* regardless of whether the transaction committed. delta_live_tuples,
154+
* tuples_inserted/updated/deleted/hot_updated/newpage_updated count attempted
155+
* actions, regardless of whether the transaction committed. delta_live_tuples,
156156
* delta_dead_tuples, and changed_tuples are set depending on commit or abort.
157157
* Note that delta_live_tuples and delta_dead_tuples can be negative!
158158
* ----------
@@ -168,6 +168,7 @@ typedef struct PgStat_TableCounts
168168
PgStat_Counter t_tuples_updated;
169169
PgStat_Counter t_tuples_deleted;
170170
PgStat_Counter t_tuples_hot_updated;
171+
PgStat_Counter t_tuples_newpage_updated;
171172
bool t_truncdropped;
172173

173174
PgStat_Counter t_delta_live_tuples;
@@ -401,6 +402,7 @@ typedef struct PgStat_StatTabEntry
401402
PgStat_Counter tuples_updated;
402403
PgStat_Counter tuples_deleted;
403404
PgStat_Counter tuples_hot_updated;
405+
PgStat_Counter tuples_newpage_updated;
404406

405407
PgStat_Counter live_tuples;
406408
PgStat_Counter dead_tuples;
@@ -616,7 +618,7 @@ extern void pgstat_report_analyze(Relation rel,
616618
} while (0)
617619

618620
extern void pgstat_count_heap_insert(Relation rel, PgStat_Counter n);
619-
extern void pgstat_count_heap_update(Relation rel, bool hot);
621+
extern void pgstat_count_heap_update(Relation rel, bool hot, bool newpage);
620622
extern void pgstat_count_heap_delete(Relation rel);
621623
extern void pgstat_count_truncate(Relation rel);
622624
extern void pgstat_update_heap_dead_tuples(Relation rel, int delta);

src/test/regress/expected/rules.out

+9-3
Original file line numberDiff line numberDiff line change
@@ -1789,6 +1789,7 @@ pg_stat_all_tables| SELECT c.oid AS relid,
17891789
pg_stat_get_tuples_updated(c.oid) AS n_tup_upd,
17901790
pg_stat_get_tuples_deleted(c.oid) AS n_tup_del,
17911791
pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
1792+
pg_stat_get_tuples_newpage_updated(c.oid) AS n_tup_newpage_upd,
17921793
pg_stat_get_live_tuples(c.oid) AS n_live_tup,
17931794
pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
17941795
pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
@@ -2146,6 +2147,7 @@ pg_stat_sys_tables| SELECT relid,
21462147
n_tup_upd,
21472148
n_tup_del,
21482149
n_tup_hot_upd,
2150+
n_tup_newpage_upd,
21492151
n_live_tup,
21502152
n_dead_tup,
21512153
n_mod_since_analyze,
@@ -2193,6 +2195,7 @@ pg_stat_user_tables| SELECT relid,
21932195
n_tup_upd,
21942196
n_tup_del,
21952197
n_tup_hot_upd,
2198+
n_tup_newpage_upd,
21962199
n_live_tup,
21972200
n_dead_tup,
21982201
n_mod_since_analyze,
@@ -2244,7 +2247,8 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid,
22442247
pg_stat_get_xact_tuples_inserted(c.oid) AS n_tup_ins,
22452248
pg_stat_get_xact_tuples_updated(c.oid) AS n_tup_upd,
22462249
pg_stat_get_xact_tuples_deleted(c.oid) AS n_tup_del,
2247-
pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd
2250+
pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
2251+
pg_stat_get_xact_tuples_newpage_updated(c.oid) AS n_tup_newpage_upd
22482252
FROM ((pg_class c
22492253
LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
22502254
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2260,7 +2264,8 @@ pg_stat_xact_sys_tables| SELECT relid,
22602264
n_tup_ins,
22612265
n_tup_upd,
22622266
n_tup_del,
2263-
n_tup_hot_upd
2267+
n_tup_hot_upd,
2268+
n_tup_newpage_upd
22642269
FROM pg_stat_xact_all_tables
22652270
WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text));
22662271
pg_stat_xact_user_functions| SELECT p.oid AS funcid,
@@ -2282,7 +2287,8 @@ pg_stat_xact_user_tables| SELECT relid,
22822287
n_tup_ins,
22832288
n_tup_upd,
22842289
n_tup_del,
2285-
n_tup_hot_upd
2290+
n_tup_hot_upd,
2291+
n_tup_newpage_upd
22862292
FROM pg_stat_xact_all_tables
22872293
WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text));
22882294
pg_statio_all_indexes| SELECT c.oid AS relid,

0 commit comments

Comments
 (0)