diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2007-01-11 23:06:16 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2007-01-11 23:06:16 +0000 |
commit | 0069d7c36acea96f6c3ca7585e9e7ce1501d8d7e (patch) | |
tree | c75bdbb27f907c6a6e608581a127cf88fce6362b | |
parent | e6be37ffe2ca736378c9c4608ee8f8b2e11189b9 (diff) | |
download | postgresql-0069d7c36acea96f6c3ca7585e9e7ce1501d8d7e.tar.gz postgresql-0069d7c36acea96f6c3ca7585e9e7ce1501d8d7e.zip |
Fix a performance problem in databases with large numbers of tables
(or other types of pg_class entry): the function pgstat_vacuum_tabstat,
invoked during VACUUM startup, had runtime proportional to the number of
stats table entries times the number of pg_class rows; in other words
O(N^2) if the stats collector's information is reasonably complete.
Replace list searching with a hash table to bring it back to O(N)
behavior. Per report from kim at myemma.com.
Back-patch as far as 8.1; 8.0 and before use different coding here.
-rw-r--r-- | src/backend/postmaster/pgstat.c | 87 |
1 files changed, 59 insertions, 28 deletions
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 76d49abeae5..d41d5c54cba 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -13,7 +13,7 @@ * * Copyright (c) 2001-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.111.2.6 2006/07/16 18:17:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.111.2.7 2007/01/11 23:06:16 tgl Exp $ * ---------- */ #include "postgres.h" @@ -178,6 +178,7 @@ static void pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, PgStat_StatBeEntry **betab, int *numbackends); static void backend_read_statsfile(void); +static HTAB *pgstat_collect_oids(Oid catalogid); static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype); static void pgstat_send(void *msg, int len); @@ -886,10 +887,7 @@ pgstat_report_tabstat(void) void pgstat_vacuum_tabstat(void) { - List *oidlist; - Relation rel; - HeapScanDesc scan; - HeapTuple tup; + HTAB *htab; PgStat_MsgTabpurge msg; HASH_SEQ_STATUS hstat; PgStat_StatDBEntry *dbentry; @@ -908,15 +906,7 @@ pgstat_vacuum_tabstat(void) /* * Read pg_database and make a list of OIDs of all existing databases */ - oidlist = NIL; - rel = heap_open(DatabaseRelationId, AccessShareLock); - scan = heap_beginscan(rel, SnapshotNow, 0, NULL); - while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL) - { - oidlist = lappend_oid(oidlist, HeapTupleGetOid(tup)); - } - heap_endscan(scan); - heap_close(rel, AccessShareLock); + htab = pgstat_collect_oids(DatabaseRelationId); /* * Search the database hash table for dead databases and tell the @@ -927,12 +917,14 @@ pgstat_vacuum_tabstat(void) { Oid dbid = dbentry->databaseid; - if (!list_member_oid(oidlist, dbid)) + CHECK_FOR_INTERRUPTS(); + + if (hash_search(htab, (void *) &dbid, HASH_FIND, NULL) == NULL) pgstat_drop_database(dbid); } /* Clean up */ - list_free(oidlist); + hash_destroy(htab); /* * Lookup our own database entry; if not found, nothing more to do. @@ -946,15 +938,7 @@ pgstat_vacuum_tabstat(void) /* * Similarly to above, make a list of all known relations in this DB. */ - oidlist = NIL; - rel = heap_open(RelationRelationId, AccessShareLock); - scan = heap_beginscan(rel, SnapshotNow, 0, NULL); - while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL) - { - oidlist = lappend_oid(oidlist, HeapTupleGetOid(tup)); - } - heap_endscan(scan); - heap_close(rel, AccessShareLock); + htab = pgstat_collect_oids(RelationRelationId); /* * Initialize our messages table counter to zero @@ -967,13 +951,17 @@ pgstat_vacuum_tabstat(void) hash_seq_init(&hstat, dbentry->tables); while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&hstat)) != NULL) { - if (list_member_oid(oidlist, tabentry->tableid)) + Oid tabid = tabentry->tableid; + + CHECK_FOR_INTERRUPTS(); + + if (hash_search(htab, (void *) &tabid, HASH_FIND, NULL) != NULL) continue; /* * Not there, so add this table's Oid to the message */ - msg.m_tableid[msg.m_nentries++] = tabentry->tableid; + msg.m_tableid[msg.m_nentries++] = tabid; /* * If the message is full, send it out and reinitialize to empty @@ -1005,7 +993,50 @@ pgstat_vacuum_tabstat(void) } /* Clean up */ - list_free(oidlist); + hash_destroy(htab); +} + + +/* ---------- + * pgstat_collect_oids() - + * + * Collect the OIDs of either all databases or all tables, according to + * the parameter, into a temporary hash table. Caller should hash_destroy + * the result when done with it. + * ---------- + */ +static HTAB * +pgstat_collect_oids(Oid catalogid) +{ + HTAB *htab; + HASHCTL hash_ctl; + Relation rel; + HeapScanDesc scan; + HeapTuple tup; + + memset(&hash_ctl, 0, sizeof(hash_ctl)); + hash_ctl.keysize = sizeof(Oid); + hash_ctl.entrysize = sizeof(Oid); + hash_ctl.hash = oid_hash; + htab = hash_create("Temporary table of OIDs", + PGSTAT_TAB_HASH_SIZE, + &hash_ctl, + HASH_ELEM | HASH_FUNCTION); + + rel = heap_open(catalogid, AccessShareLock); + scan = heap_beginscan(rel, SnapshotNow, 0, NULL); + while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + Oid thisoid = HeapTupleGetOid(tup); + + CHECK_FOR_INTERRUPTS(); + + (void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL); + } + heap_endscan(scan); + heap_close(rel, AccessShareLock); + + return htab; } |