📄 pgstat.c
字号:
* * Called from tcop/postgres.c to send the so far collected * per table access statistics to the collector. * ---------- */voidpgstat_report_tabstat(void){ int i; if (pgStatSock < 0 || !(pgstat_collect_querystring || pgstat_collect_tuplelevel || pgstat_collect_blocklevel)) { /* Not reporting stats, so just flush whatever we have */ pgStatTabstatUsed = 0; return; } /* * For each message buffer used during the last query set the header * fields and send it out. */ for (i = 0; i < pgStatTabstatUsed; i++) { PgStat_MsgTabstat *tsmsg = pgStatTabstatMessages[i]; int n; int len; n = tsmsg->m_nentries; len = offsetof(PgStat_MsgTabstat, m_entry[0]) + n * sizeof(PgStat_TableEntry); tsmsg->m_xact_commit = pgStatXactCommit; tsmsg->m_xact_rollback = pgStatXactRollback; pgStatXactCommit = 0; pgStatXactRollback = 0; pgstat_setheader(&tsmsg->m_hdr, PGSTAT_MTYPE_TABSTAT); pgstat_send(tsmsg, len); } pgStatTabstatUsed = 0;}/* ---------- * pgstat_vacuum_tabstat() - * * Will tell the collector about objects he can get rid of. * ---------- */intpgstat_vacuum_tabstat(void){ Relation dbrel; HeapScanDesc dbscan; HeapTuple dbtup; Oid *dbidlist; int dbidalloc; int dbidused; HASH_SEQ_STATUS hstat; PgStat_StatDBEntry *dbentry; PgStat_StatTabEntry *tabentry; HeapTuple reltup; int nobjects = 0; PgStat_MsgTabpurge msg; int len; int i; if (pgStatSock < 0) return 0; /* * If not done for this transaction, read the statistics collector * stats file into some hash tables. */ if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId())) { pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId, &pgStatBeTable, &pgStatNumBackends); pgStatDBHashXact = GetCurrentTransactionId(); } /* * Lookup our own database entry */ dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, (void *) &MyDatabaseId, HASH_FIND, NULL); if (dbentry == NULL) return -1; if (dbentry->tables == NULL) return 0; /* * Initialize our messages table counter to zero */ msg.m_nentries = 0; /* * Check for all tables if they still exist. */ hash_seq_init(&hstat, dbentry->tables); while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&hstat)) != NULL) { /* * Check if this relation is still alive by looking up it's * pg_class tuple in the system catalog cache. */ reltup = SearchSysCache(RELOID, ObjectIdGetDatum(tabentry->tableid), 0, 0, 0); if (HeapTupleIsValid(reltup)) { ReleaseSysCache(reltup); continue; } /* * Add this tables Oid to the message */ msg.m_tableid[msg.m_nentries++] = tabentry->tableid; nobjects++; /* * If the message is full, send it out and reinitialize ot zero */ if (msg.m_nentries >= PGSTAT_NUM_TABPURGE) { len = offsetof(PgStat_MsgTabpurge, m_tableid[0]) +msg.m_nentries * sizeof(Oid); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE); pgstat_send(&msg, len); msg.m_nentries = 0; } } /* * Send the rest */ if (msg.m_nentries > 0) { len = offsetof(PgStat_MsgTabpurge, m_tableid[0]) +msg.m_nentries * sizeof(Oid); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE); pgstat_send(&msg, len); } /* * Read pg_database and remember the Oid's of all existing databases */ dbidalloc = 256; dbidused = 0; dbidlist = (Oid *) palloc(sizeof(Oid) * dbidalloc); dbrel = heap_openr(DatabaseRelationName, AccessShareLock); dbscan = heap_beginscan(dbrel, SnapshotNow, 0, NULL); while ((dbtup = heap_getnext(dbscan, ForwardScanDirection)) != NULL) { if (dbidused >= dbidalloc) { dbidalloc *= 2; dbidlist = (Oid *) repalloc((char *) dbidlist, sizeof(Oid) * dbidalloc); } dbidlist[dbidused++] = HeapTupleGetOid(dbtup); } heap_endscan(dbscan); heap_close(dbrel, AccessShareLock); /* * Search the database hash table for dead databases and tell the * collector to drop them as well. */ hash_seq_init(&hstat, pgStatDBHash); while ((dbentry = (PgStat_StatDBEntry *) hash_seq_search(&hstat)) != NULL) { Oid dbid = dbentry->databaseid; for (i = 0; i < dbidused; i++) { if (dbidlist[i] == dbid) { dbid = InvalidOid; break; } } if (dbid != InvalidOid) { nobjects++; pgstat_drop_database(dbid); } } /* * Free the dbid list. */ pfree((char *) dbidlist); /* * Tell the caller how many removeable objects we found */ return nobjects;}/* ---------- * pgstat_drop_database() - * * Tell the collector that we just dropped a database. * This is the only message that shouldn't get lost in space. Otherwise * the collector will keep the statistics for the dead DB until his * stats file got removed while the postmaster is down. * ---------- */static voidpgstat_drop_database(Oid databaseid){ PgStat_MsgDropdb msg; if (pgStatSock < 0) return; msg.m_databaseid = databaseid; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_DROPDB); pgstat_send(&msg, sizeof(msg));}/* ---------- * pgstat_reset_counters() - * * Tell the statistics collector to reset counters for our database. * ---------- */voidpgstat_reset_counters(void){ PgStat_MsgResetcounter msg; if (pgStatSock < 0) return; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to reset statistics counters"))); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETCOUNTER); pgstat_send(&msg, sizeof(msg));}/* ---------- * pgstat_ping() - * * Send some junk data to the collector to increase traffic. * ---------- */voidpgstat_ping(void){ PgStat_MsgDummy msg; if (pgStatSock < 0) return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_DUMMY); pgstat_send(&msg, sizeof(msg));}/* * Create or enlarge the pgStatTabstatMessages array */static boolmore_tabstat_space(void){ PgStat_MsgTabstat *newMessages; PgStat_MsgTabstat **msgArray; int newAlloc = pgStatTabstatAlloc + TABSTAT_QUANTUM; int i; /* Create (another) quantum of message buffers */ newMessages = (PgStat_MsgTabstat *) malloc(sizeof(PgStat_MsgTabstat) * TABSTAT_QUANTUM); if (newMessages == NULL) { ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); return false; } /* Create or enlarge the pointer array */ if (pgStatTabstatMessages == NULL) msgArray = (PgStat_MsgTabstat **) malloc(sizeof(PgStat_MsgTabstat *) * newAlloc); else msgArray = (PgStat_MsgTabstat **) realloc(pgStatTabstatMessages, sizeof(PgStat_MsgTabstat *) * newAlloc); if (msgArray == NULL) { free(newMessages); ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); return false; } MemSet(newMessages, 0, sizeof(PgStat_MsgTabstat) * TABSTAT_QUANTUM); for (i = 0; i < TABSTAT_QUANTUM; i++) msgArray[pgStatTabstatAlloc + i] = newMessages++; pgStatTabstatMessages = msgArray; pgStatTabstatAlloc = newAlloc; return true;}/* ---------- * pgstat_initstats() - * * Called from various places usually dealing with initialization * of Relation or Scan structures. The data placed into these * structures from here tell where later to count for buffer reads, * scans and tuples fetched. * ---------- */voidpgstat_initstats(PgStat_Info *stats, Relation rel){ Oid rel_id = rel->rd_id; PgStat_TableEntry *useent; PgStat_MsgTabstat *tsmsg; int mb; int i; /* * Initialize data not to count at all. */ stats->tabentry = NULL; stats->no_stats = FALSE; stats->heap_scan_counted = FALSE; stats->index_scan_counted = FALSE; if (pgStatSock < 0 || !(pgstat_collect_tuplelevel || pgstat_collect_blocklevel)) { stats->no_stats = TRUE; return; } /* * Search the already-used message slots for this relation. */ for (mb = 0; mb < pgStatTabstatUsed; mb++) { tsmsg = pgStatTabstatMessages[mb]; for (i = tsmsg->m_nentries; --i >= 0; ) { if (tsmsg->m_entry[i].t_id == rel_id) { stats->tabentry = (void *) &(tsmsg->m_entry[i]); return; } } if (tsmsg->m_nentries >= PGSTAT_NUM_TABENTRIES) continue; /* * Not found, but found a message buffer with an empty slot * instead. Fine, let's use this one. */ i = tsmsg->m_nentries++; useent = &tsmsg->m_entry[i]; MemSet(useent, 0, sizeof(PgStat_TableEntry)); useent->t_id = rel_id; stats->tabentry = (void *) useent; return; } /* * If we ran out of message buffers, we just allocate more. */ if (pgStatTabstatUsed >= pgStatTabstatAlloc) { if (!more_tabstat_space()) { stats->no_stats = TRUE; return; } Assert(pgStatTabstatUsed < pgStatTabstatAlloc); } /* * Use the first entry of the next message buffer. */ mb = pgStatTabstatUsed++; tsmsg = pgStatTabstatMessages[mb]; tsmsg->m_nentries = 1; useent = &tsmsg->m_entry[0]; MemSet(useent, 0, sizeof(PgStat_TableEntry)); useent->t_id = rel_id; stats->tabentry = (void *) useent;}/* ---------- * pgstat_count_xact_commit() - * * Called from access/transam/xact.c to count transaction commits. * ---------- */voidpgstat_count_xact_commit(void){ if (!(pgstat_collect_querystring || pgstat_collect_tuplelevel || pgstat_collect_blocklevel)) return; pgStatXactCommit++; /* * If there was no relation activity yet, just make one existing * message buffer used without slots, causing the next report to tell * new xact-counters. */ if (pgStatTabstatAlloc == 0) { if (!more_tabstat_space()) return; } if (pgStatTabstatUsed == 0) { pgStatTabstatUsed++; pgStatTabstatMessages[0]->m_nentries = 0; }}/* ---------- * pgstat_count_xact_rollback() - * * Called from access/transam/xact.c to count transaction rollbacks. * ---------- */voidpgstat_count_xact_rollback(void){ if (!(pgstat_collect_querystring || pgstat_collect_tuplelevel || pgstat_collect_blocklevel)) return; pgStatXactRollback++; /* * If there was no relation activity yet, just make one existing * message buffer used without slots, causing the next report to tell * new xact-counters. */ if (pgStatTabstatAlloc == 0) { if (!more_tabstat_space()) return; } if (pgStatTabstatUsed == 0) { pgStatTabstatUsed++; pgStatTabstatMessages[0]->m_nentries = 0; }}/* ---------- * pgstat_fetch_stat_dbentry() - * * Support function for the SQL-callable pgstat* functions. Returns * the collected statistics for one database or NULL. NULL doesn't mean * that the database doesn't exist, it is just not yet known by the * collector, so the caller is better off to report ZERO instead. * ---------- */PgStat_StatDBEntry *pgstat_fetch_stat_dbentry(Oid dbid){ PgStat_StatDBEntry *dbentry; /* * If not done for this transaction, read the statistics collector * stats file into some hash tables. Be careful with the * read_statsfile() call below! */ if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId())) { pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId, &pgStatBeTable, &pgStatNumBackends); pgStatDBHashXact = GetCurrentTransactionId(); } /* * Lookup the requested database */ dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, (void *) &dbid, HASH_FIND, NULL); if (dbentry == NULL) return NULL; return dbentry;}/* ---------- * pgstat_fetch_stat_tabentry() - * * Support function for the SQL-callable pgstat* functions. Returns * the collected statistics for one table or NULL. NULL doesn't mean * that the table doesn't exist, it is just not yet known by the * collector, so the caller is better off to report ZERO instead. * ---------- */PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid relid){ PgStat_StatDBEntry *dbentry; PgStat_StatTabEntry *tabentry; /* * If not done for this transaction, read the statistics collector * stats file into some hash tables. Be careful with the * read_statsfile() call below! */ if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId())) { pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId, &pgStatBeTable, &pgStatNumBackends); pgStatDBHashXact = GetCurrentTransactionId(); } /* * Lookup our database. */ dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, (void *) &MyDatabaseId, HASH_FIND, NULL); if (dbentry == NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -