Skip to content

Commit 97903c3

Browse files
committed
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.
1 parent 87f6d64 commit 97903c3

File tree

1 file changed

+59
-28
lines changed

1 file changed

+59
-28
lines changed

src/backend/postmaster/pgstat.c

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*
1414
* Copyright (c) 2001-2007, PostgreSQL Global Development Group
1515
*
16-
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.142 2007/01/05 22:19:36 momjian Exp $
16+
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.143 2007/01/11 23:06:03 tgl Exp $
1717
* ----------
1818
*/
1919
#include "postgres.h"
@@ -159,6 +159,7 @@ static void pgstat_write_statsfile(void);
159159
static void pgstat_read_statsfile(HTAB **dbhash, Oid onlydb);
160160
static void backend_read_statsfile(void);
161161
static void pgstat_read_current_status(void);
162+
static HTAB *pgstat_collect_oids(Oid catalogid);
162163

163164
static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype);
164165
static void pgstat_send(void *msg, int len);
@@ -657,10 +658,7 @@ pgstat_report_tabstat(void)
657658
void
658659
pgstat_vacuum_tabstat(void)
659660
{
660-
List *oidlist;
661-
Relation rel;
662-
HeapScanDesc scan;
663-
HeapTuple tup;
661+
HTAB *htab;
664662
PgStat_MsgTabpurge msg;
665663
HASH_SEQ_STATUS hstat;
666664
PgStat_StatDBEntry *dbentry;
@@ -679,15 +677,7 @@ pgstat_vacuum_tabstat(void)
679677
/*
680678
* Read pg_database and make a list of OIDs of all existing databases
681679
*/
682-
oidlist = NIL;
683-
rel = heap_open(DatabaseRelationId, AccessShareLock);
684-
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
685-
while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
686-
{
687-
oidlist = lappend_oid(oidlist, HeapTupleGetOid(tup));
688-
}
689-
heap_endscan(scan);
690-
heap_close(rel, AccessShareLock);
680+
htab = pgstat_collect_oids(DatabaseRelationId);
691681

692682
/*
693683
* Search the database hash table for dead databases and tell the
@@ -698,12 +688,14 @@ pgstat_vacuum_tabstat(void)
698688
{
699689
Oid dbid = dbentry->databaseid;
700690

701-
if (!list_member_oid(oidlist, dbid))
691+
CHECK_FOR_INTERRUPTS();
692+
693+
if (hash_search(htab, (void *) &dbid, HASH_FIND, NULL) == NULL)
702694
pgstat_drop_database(dbid);
703695
}
704696

705697
/* Clean up */
706-
list_free(oidlist);
698+
hash_destroy(htab);
707699

708700
/*
709701
* Lookup our own database entry; if not found, nothing more to do.
@@ -717,15 +709,7 @@ pgstat_vacuum_tabstat(void)
717709
/*
718710
* Similarly to above, make a list of all known relations in this DB.
719711
*/
720-
oidlist = NIL;
721-
rel = heap_open(RelationRelationId, AccessShareLock);
722-
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
723-
while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
724-
{
725-
oidlist = lappend_oid(oidlist, HeapTupleGetOid(tup));
726-
}
727-
heap_endscan(scan);
728-
heap_close(rel, AccessShareLock);
712+
htab = pgstat_collect_oids(RelationRelationId);
729713

730714
/*
731715
* Initialize our messages table counter to zero
@@ -738,13 +722,17 @@ pgstat_vacuum_tabstat(void)
738722
hash_seq_init(&hstat, dbentry->tables);
739723
while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&hstat)) != NULL)
740724
{
741-
if (list_member_oid(oidlist, tabentry->tableid))
725+
Oid tabid = tabentry->tableid;
726+
727+
CHECK_FOR_INTERRUPTS();
728+
729+
if (hash_search(htab, (void *) &tabid, HASH_FIND, NULL) != NULL)
742730
continue;
743731

744732
/*
745733
* Not there, so add this table's Oid to the message
746734
*/
747-
msg.m_tableid[msg.m_nentries++] = tabentry->tableid;
735+
msg.m_tableid[msg.m_nentries++] = tabid;
748736

749737
/*
750738
* If the message is full, send it out and reinitialize to empty
@@ -776,7 +764,50 @@ pgstat_vacuum_tabstat(void)
776764
}
777765

778766
/* Clean up */
779-
list_free(oidlist);
767+
hash_destroy(htab);
768+
}
769+
770+
771+
/* ----------
772+
* pgstat_collect_oids() -
773+
*
774+
* Collect the OIDs of either all databases or all tables, according to
775+
* the parameter, into a temporary hash table. Caller should hash_destroy
776+
* the result when done with it.
777+
* ----------
778+
*/
779+
static HTAB *
780+
pgstat_collect_oids(Oid catalogid)
781+
{
782+
HTAB *htab;
783+
HASHCTL hash_ctl;
784+
Relation rel;
785+
HeapScanDesc scan;
786+
HeapTuple tup;
787+
788+
memset(&hash_ctl, 0, sizeof(hash_ctl));
789+
hash_ctl.keysize = sizeof(Oid);
790+
hash_ctl.entrysize = sizeof(Oid);
791+
hash_ctl.hash = oid_hash;
792+
htab = hash_create("Temporary table of OIDs",
793+
PGSTAT_TAB_HASH_SIZE,
794+
&hash_ctl,
795+
HASH_ELEM | HASH_FUNCTION);
796+
797+
rel = heap_open(catalogid, AccessShareLock);
798+
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
799+
while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
800+
{
801+
Oid thisoid = HeapTupleGetOid(tup);
802+
803+
CHECK_FOR_INTERRUPTS();
804+
805+
(void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
806+
}
807+
heap_endscan(scan);
808+
heap_close(rel, AccessShareLock);
809+
810+
return htab;
780811
}
781812

782813

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy