Improve performance of find_tabstat_entry()/get_tabstat_entry()
Patch introduces a hash map reloid -> PgStat_TableStatus which improves performance in case of large number of tables/partitions. Author: Aleksander Alekseev Reviewed-by: Andres Freund, Anastasia Lubennikova, Tels, me https://commitfest.postgresql.org/13/1058/
This commit is contained in:
parent
d65561464f
commit
090010f2ec
@ -173,6 +173,20 @@ typedef struct TabStatusArray
|
|||||||
|
|
||||||
static TabStatusArray *pgStatTabList = NULL;
|
static TabStatusArray *pgStatTabList = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pgStatTabHash entry
|
||||||
|
*/
|
||||||
|
typedef struct TabStatHashEntry
|
||||||
|
{
|
||||||
|
Oid t_id;
|
||||||
|
PgStat_TableStatus* tsa_entry;
|
||||||
|
} TabStatHashEntry;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hash table for O(1) t_id -> tsa_entry lookup
|
||||||
|
*/
|
||||||
|
static HTAB *pgStatTabHash = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Backends store per-function info that's waiting to be sent to the collector
|
* Backends store per-function info that's waiting to be sent to the collector
|
||||||
* in this hash table (indexed by function OID).
|
* in this hash table (indexed by function OID).
|
||||||
@ -840,6 +854,14 @@ pgstat_report_stat(bool force)
|
|||||||
tsa->tsa_used = 0;
|
tsa->tsa_used = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pgStatTabHash is outdated on this point so we have to clean it,
|
||||||
|
* hash_destroy() will remove hash memory context, allocated in
|
||||||
|
* make_sure_stat_tab_initialized()
|
||||||
|
*/
|
||||||
|
hash_destroy(pgStatTabHash);
|
||||||
|
pgStatTabHash = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send partial messages. Make sure that any pending xact commit/abort
|
* Send partial messages. Make sure that any pending xact commit/abort
|
||||||
* gets counted, even if there are no table stats to send.
|
* gets counted, even if there are no table stats to send.
|
||||||
@ -1684,60 +1706,88 @@ pgstat_initstats(Relation rel)
|
|||||||
rel->pgstat_info = get_tabstat_entry(rel_id, rel->rd_rel->relisshared);
|
rel->pgstat_info = get_tabstat_entry(rel_id, rel->rd_rel->relisshared);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure pgStatTabList and pgStatTabHash are initialized.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
make_sure_stat_tab_initialized()
|
||||||
|
{
|
||||||
|
HASHCTL ctl;
|
||||||
|
MemoryContext new_ctx;
|
||||||
|
|
||||||
|
if(!pgStatTabList)
|
||||||
|
{
|
||||||
|
/* This is first time procedure is called */
|
||||||
|
pgStatTabList = (TabStatusArray *) MemoryContextAllocZero(TopMemoryContext,
|
||||||
|
sizeof(TabStatusArray));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pgStatTabHash)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Hash table was freed or never existed. */
|
||||||
|
|
||||||
|
new_ctx = AllocSetContextCreate(
|
||||||
|
TopMemoryContext,
|
||||||
|
"PGStatLookupHashTableContext",
|
||||||
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
|
|
||||||
|
memset(&ctl, 0, sizeof(ctl));
|
||||||
|
ctl.keysize = sizeof(Oid);
|
||||||
|
ctl.entrysize = sizeof(TabStatHashEntry);
|
||||||
|
ctl.hcxt = new_ctx;
|
||||||
|
|
||||||
|
pgStatTabHash = hash_create("pgstat t_id to tsa_entry lookup hash table",
|
||||||
|
TABSTAT_QUANTUM, &ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_tabstat_entry - find or create a PgStat_TableStatus entry for rel
|
* get_tabstat_entry - find or create a PgStat_TableStatus entry for rel
|
||||||
*/
|
*/
|
||||||
static PgStat_TableStatus *
|
static PgStat_TableStatus *
|
||||||
get_tabstat_entry(Oid rel_id, bool isshared)
|
get_tabstat_entry(Oid rel_id, bool isshared)
|
||||||
{
|
{
|
||||||
|
TabStatHashEntry* hash_entry;
|
||||||
PgStat_TableStatus *entry;
|
PgStat_TableStatus *entry;
|
||||||
TabStatusArray *tsa;
|
TabStatusArray *tsa;
|
||||||
TabStatusArray *prev_tsa;
|
bool found;
|
||||||
int i;
|
|
||||||
|
make_sure_stat_tab_initialized();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Search the already-used tabstat slots for this relation.
|
* Find an entry or create a new one.
|
||||||
*/
|
*/
|
||||||
prev_tsa = NULL;
|
hash_entry = hash_search(pgStatTabHash, &rel_id, HASH_ENTER, &found);
|
||||||
for (tsa = pgStatTabList; tsa != NULL; prev_tsa = tsa, tsa = tsa->tsa_next)
|
if(found)
|
||||||
|
return hash_entry->tsa_entry;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* `hash_entry` was just created and now we have to fill it.
|
||||||
|
* First make sure there is a free space in a last element of pgStatTabList.
|
||||||
|
*/
|
||||||
|
tsa = pgStatTabList;
|
||||||
|
while(tsa->tsa_used == TABSTAT_QUANTUM)
|
||||||
{
|
{
|
||||||
for (i = 0; i < tsa->tsa_used; i++)
|
if(tsa->tsa_next == NULL)
|
||||||
{
|
{
|
||||||
entry = &tsa->tsa_entries[i];
|
tsa->tsa_next = (TabStatusArray *) MemoryContextAllocZero(TopMemoryContext,
|
||||||
if (entry->t_id == rel_id)
|
sizeof(TabStatusArray));
|
||||||
return entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tsa->tsa_used < TABSTAT_QUANTUM)
|
tsa = tsa->tsa_next;
|
||||||
{
|
|
||||||
/*
|
|
||||||
* It must not be present, but we found a free slot instead. Fine,
|
|
||||||
* let's use this one. We assume the entry was already zeroed,
|
|
||||||
* either at creation or after last use.
|
|
||||||
*/
|
|
||||||
entry = &tsa->tsa_entries[tsa->tsa_used++];
|
|
||||||
entry->t_id = rel_id;
|
|
||||||
entry->t_shared = isshared;
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We ran out of tabstat slots, so allocate more. Be sure they're zeroed.
|
* Add an entry.
|
||||||
*/
|
|
||||||
tsa = (TabStatusArray *) MemoryContextAllocZero(TopMemoryContext,
|
|
||||||
sizeof(TabStatusArray));
|
|
||||||
if (prev_tsa)
|
|
||||||
prev_tsa->tsa_next = tsa;
|
|
||||||
else
|
|
||||||
pgStatTabList = tsa;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Use the first entry of the new TabStatusArray.
|
|
||||||
*/
|
*/
|
||||||
entry = &tsa->tsa_entries[tsa->tsa_used++];
|
entry = &tsa->tsa_entries[tsa->tsa_used++];
|
||||||
entry->t_id = rel_id;
|
entry->t_id = rel_id;
|
||||||
entry->t_shared = isshared;
|
entry->t_shared = isshared;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a corresponding entry to pgStatTabHash.
|
||||||
|
*/
|
||||||
|
hash_entry->tsa_entry = entry;
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1749,22 +1799,19 @@ get_tabstat_entry(Oid rel_id, bool isshared)
|
|||||||
PgStat_TableStatus *
|
PgStat_TableStatus *
|
||||||
find_tabstat_entry(Oid rel_id)
|
find_tabstat_entry(Oid rel_id)
|
||||||
{
|
{
|
||||||
PgStat_TableStatus *entry;
|
TabStatHashEntry* hash_entry;
|
||||||
TabStatusArray *tsa;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (tsa = pgStatTabList; tsa != NULL; tsa = tsa->tsa_next)
|
/*
|
||||||
{
|
* There are no entries at all.
|
||||||
for (i = 0; i < tsa->tsa_used; i++)
|
*/
|
||||||
{
|
if(!pgStatTabHash)
|
||||||
entry = &tsa->tsa_entries[i];
|
return NULL;
|
||||||
if (entry->t_id == rel_id)
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Not present */
|
hash_entry = hash_search(pgStatTabHash, &rel_id, HASH_FIND, NULL);
|
||||||
return NULL;
|
if(!hash_entry)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return hash_entry->tsa_entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user