diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index b4fcbaf..0ae885b 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1907,6 +1907,13 @@ include 'filename' results in most cases. + + When you see pg_stat_walwriter.dirty_write, which means number + of buffer flushing at buffer full, is continuously increasing + in your running server, you may need to enlarge this buffer + size. + + diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 39ccfbb..3117f91 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -278,6 +278,14 @@ postgres: user database host + pg_stat_walwriterpg_stat_walwriter + One row only, showing statistics about the wal writer + process's activity. See + for details. + + + + pg_stat_databasepg_stat_database One row per database, showing database-wide statistics. See for details. @@ -735,6 +743,39 @@ postgres: user database host + + <structname>pg_stat_walwriter</structname> View + + + + + Column + Type + Description + + + + + + dirty_writes + bigint + Number of dirty writes, which means flushing wal buffers + because of its full. + + + stats_reset + timestamp with time zone + Time at which these statistics were last reset + + + +
+ + + The pg_stat_walwriter view will always have a + single row, containing global data for the cluster. + + <structname>pg_stat_database</structname> View diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index d251d08..631a0af 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -1347,6 +1347,7 @@ AdvanceXLInsertBuffer(bool new_segment) WriteRqst.Write = OldPageRqstPtr; WriteRqst.Flush = 0; XLogWrite(WriteRqst, false, false); + WalWriterStats.m_xlog_dirty_writes++; LWLockRelease(WALWriteLock); TRACE_POSTGRESQL_WAL_BUFFER_WRITE_DIRTY_DONE(); } diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 607a72f..40f0c34 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -671,6 +671,11 @@ CREATE VIEW pg_stat_bgwriter AS pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; +CREATE VIEW pg_stat_walwriter AS + SELECT + pg_stat_get_xlog_dirty_writes() AS dirty_writes, + pg_stat_get_wal_stat_reset_time() AS stats_reset; + CREATE VIEW pg_user_mappings AS SELECT U.oid AS umid, diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index be3adf1..5be78c6 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -125,6 +125,15 @@ char *pgstat_stat_tmpname = NULL; */ PgStat_MsgBgWriter BgWriterStats; +/* + * WalWriter global statistics counter. + * Despite its name, this counter is actually used not only in walwriter, + * but also in each backend process to sum up xlog dirty writes. + * Those processes would increment this counter in each XLogWrite call, + * then send it to the stat collector process. + */ +PgStat_MsgWalWriter WalWriterStats; + /* ---------- * Local data * ---------- @@ -279,6 +288,7 @@ static void pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len); static void pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len); static void pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len); static void pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len); +static void pgstat_recv_walwriter(PgStat_MsgWalWriter *msg, int len); static void pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len); static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len); static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len); @@ -762,6 +772,9 @@ pgstat_report_stat(bool force) /* Now, send function statistics */ pgstat_send_funcstats(); + + /* Now, send wal buffer flush statistics */ + pgstat_send_walwriter(); } /* @@ -1188,11 +1201,13 @@ pgstat_reset_shared_counters(const char *target) if (strcmp(target, "bgwriter") == 0) msg.m_resettarget = RESET_BGWRITER; + else if (strcmp(target, "walwriter") == 0) + msg.m_resettarget = RESET_WALWRITER; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized reset target: \"%s\"", target), - errhint("Target must be \"bgwriter\"."))); + errhint("Target must be \"bgwriter\" or \"walwriter\"."))); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER); pgstat_send(&msg, sizeof(msg)); @@ -2988,6 +3003,38 @@ pgstat_send_bgwriter(void) MemSet(&BgWriterStats, 0, sizeof(BgWriterStats)); } +/* ---------- + * pgstat_send_walwriter() - + * + * Send walwriter statistics to the collector + * ---------- + */ +void +pgstat_send_walwriter(void) +{ + /* We assume this initializes to zeroes */ + static const PgStat_MsgBgWriter all_zeroes; + + /* + * This function can be called even if nothing at all has happened. In + * this case, avoid sending a completely empty message to the stats + * collector. + */ + if (memcmp(&WalWriterStats, &all_zeroes, sizeof(PgStat_MsgWalWriter)) == 0) + return; + + /* + * Prepare and send the message + */ + pgstat_setheader(&WalWriterStats.m_hdr, PGSTAT_MTYPE_WALWRITER); + pgstat_send(&WalWriterStats, sizeof(WalWriterStats)); + + /* + * Clear out the statistics buffer, so it can be re-used. + */ + MemSet(&WalWriterStats, 0, sizeof(WalWriterStats)); +} + /* ---------- * PgstatCollectorMain() - @@ -3209,6 +3256,10 @@ PgstatCollectorMain(int argc, char *argv[]) pgstat_recv_bgwriter((PgStat_MsgBgWriter *) &msg, len); break; + case PGSTAT_MTYPE_WALWRITER: + pgstat_recv_walwriter((PgStat_MsgWalWriter *) &msg, len); + break; + case PGSTAT_MTYPE_FUNCSTAT: pgstat_recv_funcstat((PgStat_MsgFuncstat *) &msg, len); break; @@ -3638,7 +3689,8 @@ pgstat_read_statsfile(Oid onlydb, bool permanent) * Set the current timestamp (will be kept only in case we can't load an * existing statsfile). */ - globalStats.stat_reset_timestamp = GetCurrentTimestamp(); + globalStats.stat_bgw_reset_timestamp = GetCurrentTimestamp(); + globalStats.stat_wal_reset_timestamp = GetCurrentTimestamp(); /* * Try to open the status file. If it doesn't exist, the backends simply @@ -4381,8 +4433,23 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len) if (msg->m_resettarget == RESET_BGWRITER) { /* Reset the global background writer statistics for the cluster. */ - memset(&globalStats, 0, sizeof(globalStats)); - globalStats.stat_reset_timestamp = GetCurrentTimestamp(); + globalStats.timed_checkpoints = 0; + globalStats.requested_checkpoints = 0; + globalStats.checkpoint_write_time = 0; + globalStats.checkpoint_sync_time = 0; + globalStats.buf_written_checkpoints = 0; + globalStats.buf_written_clean = 0; + globalStats.maxwritten_clean = 0; + globalStats.buf_written_backend = 0; + globalStats.buf_fsync_backend = 0; + globalStats.buf_alloc = 0; + globalStats.stat_bgw_reset_timestamp = GetCurrentTimestamp(); + } + else if (msg->m_resettarget == RESET_WALWRITER) + { + /* Reset the global walwriter statistics for the cluster. */ + globalStats.xlog_dirty_writes = 0; + globalStats.stat_wal_reset_timestamp = GetCurrentTimestamp(); } /* @@ -4536,6 +4603,18 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len) } /* ---------- + * pgstat_recv_walwriter() - + * + * Process a WALWRITER message. + * ---------- + */ +static void +pgstat_recv_walwriter(PgStat_MsgWalWriter *msg, int len) +{ + globalStats.xlog_dirty_writes += msg->m_xlog_dirty_writes; +} + +/* ---------- * pgstat_recv_recoveryconflict() - * * Process a RECOVERYCONFLICT message. diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c index c3e15ef..cd294fb 100644 --- a/src/backend/postmaster/walwriter.c +++ b/src/backend/postmaster/walwriter.c @@ -290,6 +290,8 @@ WalWriterMain(void) else if (left_till_hibernate > 0) left_till_hibernate--; + pgstat_send_walwriter(); + /* * Sleep until we are signaled or WalWriterDelay has elapsed. If we * haven't done anything useful for quite some time, lengthen the diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 7d4059f..fc25dda 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -97,6 +97,7 @@ extern Datum pg_stat_get_bgwriter_stat_reset_time(PG_FUNCTION_ARGS); extern Datum pg_stat_get_buf_written_backend(PG_FUNCTION_ARGS); extern Datum pg_stat_get_buf_fsync_backend(PG_FUNCTION_ARGS); extern Datum pg_stat_get_buf_alloc(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_wal_stat_reset_time(PG_FUNCTION_ARGS); extern Datum pg_stat_get_xact_numscans(PG_FUNCTION_ARGS); extern Datum pg_stat_get_xact_tuples_returned(PG_FUNCTION_ARGS); @@ -118,6 +119,8 @@ extern Datum pg_stat_reset_shared(PG_FUNCTION_ARGS); extern Datum pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS); extern Datum pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_xlog_dirty_writes(PG_FUNCTION_ARGS); + /* Global bgwriter statistics, from bgwriter.c */ extern PgStat_MsgBgWriter bgwriterStats; @@ -1443,7 +1446,7 @@ pg_stat_get_checkpoint_sync_time(PG_FUNCTION_ARGS) Datum pg_stat_get_bgwriter_stat_reset_time(PG_FUNCTION_ARGS) { - PG_RETURN_TIMESTAMPTZ(pgstat_fetch_global()->stat_reset_timestamp); + PG_RETURN_TIMESTAMPTZ(pgstat_fetch_global()->stat_bgw_reset_timestamp); } Datum @@ -1465,6 +1468,12 @@ pg_stat_get_buf_alloc(PG_FUNCTION_ARGS) } Datum +pg_stat_get_wal_stat_reset_time(PG_FUNCTION_ARGS) +{ + PG_RETURN_TIMESTAMPTZ(pgstat_fetch_global()->stat_wal_reset_timestamp); +} + +Datum pg_stat_get_xact_numscans(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); @@ -1701,3 +1710,9 @@ pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + +Datum +pg_stat_get_xlog_dirty_writes(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_global()->xlog_dirty_writes); +} diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index f935eb1..d6edb5a 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -2702,6 +2702,8 @@ DATA(insert OID = 3063 ( pg_stat_get_buf_fsync_backend PGNSP PGUID 12 1 0 0 0 f DESCR("statistics: number of backend buffer writes that did their own fsync"); DATA(insert OID = 2859 ( pg_stat_get_buf_alloc PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 20 "" _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ )); DESCR("statistics: number of buffer allocations"); +DATA(insert OID = 2860 ( pg_stat_get_wal_stat_reset_time PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 1184 "" _null_ _null_ _null_ _null_ pg_stat_get_wal_stat_reset_time _null_ _null_ _null_ )); +DESCR("statistics: last reset for the wal"); DATA(insert OID = 2978 ( pg_stat_get_function_calls PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ )); DESCR("statistics: number of function calls"); @@ -2746,6 +2748,9 @@ DESCR("statistics: reset collected statistics for a single table or index in the DATA(insert OID = 3777 ( pg_stat_reset_single_function_counters PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2278 "26" _null_ _null_ _null_ _null_ pg_stat_reset_single_function_counters _null_ _null_ _null_ )); DESCR("statistics: reset collected statistics for a single function in the current database"); +DATA(insert OID = 3766 ( pg_stat_get_xlog_dirty_writes PGNSP PGUID 12 1 0 0 0 f f f f f f v 0 0 20 "" _null_ _null_ _null_ _null_ pg_stat_get_xlog_dirty_writes _null_ _null_ _null_ )); +DESCR("statistics: get xlog dirty buffer write statistics"); + DATA(insert OID = 3163 ( pg_trigger_depth PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_trigger_depth _null_ _null_ _null_ )); DESCR("current trigger depth"); diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 613c1c2..f71c538 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -45,6 +45,7 @@ typedef enum StatMsgType PGSTAT_MTYPE_VACUUM, PGSTAT_MTYPE_ANALYZE, PGSTAT_MTYPE_BGWRITER, + PGSTAT_MTYPE_WALWRITER, PGSTAT_MTYPE_FUNCSTAT, PGSTAT_MTYPE_FUNCPURGE, PGSTAT_MTYPE_RECOVERYCONFLICT, @@ -102,7 +103,8 @@ typedef struct PgStat_TableCounts /* Possible targets for resetting cluster-wide shared values */ typedef enum PgStat_Shared_Reset_Target { - RESET_BGWRITER + RESET_BGWRITER, + RESET_WALWRITER } PgStat_Shared_Reset_Target; /* Possible object types for resetting single counters */ @@ -372,6 +374,17 @@ typedef struct PgStat_MsgBgWriter } PgStat_MsgBgWriter; /* ---------- + * PgStat_MsgWalWriter Sent by the walwriter to update statistics. + * ---------- + */ +typedef struct PgStat_MsgWalWriter +{ + PgStat_MsgHdr m_hdr; + + PgStat_Counter m_xlog_dirty_writes; +} PgStat_MsgWalWriter; + +/* ---------- * PgStat_MsgRecoveryConflict Sent by the backend upon recovery conflict * ---------- */ @@ -499,6 +512,7 @@ typedef union PgStat_Msg PgStat_MsgVacuum msg_vacuum; PgStat_MsgAnalyze msg_analyze; PgStat_MsgBgWriter msg_bgwriter; + PgStat_MsgWalWriter msg_walwriter; PgStat_MsgFuncstat msg_funcstat; PgStat_MsgFuncpurge msg_funcpurge; PgStat_MsgRecoveryConflict msg_recoveryconflict; @@ -622,7 +636,10 @@ typedef struct PgStat_GlobalStats PgStat_Counter buf_written_backend; PgStat_Counter buf_fsync_backend; PgStat_Counter buf_alloc; - TimestampTz stat_reset_timestamp; + TimestampTz stat_bgw_reset_timestamp; + + PgStat_Counter xlog_dirty_writes; + TimestampTz stat_wal_reset_timestamp; } PgStat_GlobalStats; @@ -730,6 +747,8 @@ extern char *pgstat_stat_filename; */ extern PgStat_MsgBgWriter BgWriterStats; +extern PgStat_MsgWalWriter WalWriterStats; + /* * Updated by pgstat_count_buffer_*_time macros */ @@ -858,6 +877,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info, void *recdata, uint32 len); extern void pgstat_send_bgwriter(void); +extern void pgstat_send_walwriter(void); /* ---------- * Support functions for the SQL-callable functions to