Skip to content

Commit 21025d4

Browse files
committed
Use a hash table to store current sequence values.
This speeds up nextval() and currval(), when you touch a lot of different sequences in the same backend. David Rowley
1 parent 982b82d commit 21025d4

File tree

1 file changed

+35
-36
lines changed

1 file changed

+35
-36
lines changed

src/backend/commands/sequence.c

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,10 @@ typedef struct sequence_magic
6060
* session. This is needed to hold onto nextval/currval state. (We can't
6161
* rely on the relcache, since it's only, well, a cache, and may decide to
6262
* discard entries.)
63-
*
64-
* XXX We use linear search to find pre-existing SeqTable entries. This is
65-
* good when only a small number of sequences are touched in a session, but
66-
* would suck with many different sequences. Perhaps use a hashtable someday.
6763
*/
6864
typedef struct SeqTableData
6965
{
70-
struct SeqTableData *next; /* link to next SeqTable object */
71-
Oid relid; /* pg_class OID of this sequence */
66+
Oid relid; /* pg_class OID of this sequence (hash key) */
7267
Oid filenode; /* last seen relfilenode of this sequence */
7368
LocalTransactionId lxid; /* xact in which we last did a seq op */
7469
bool last_valid; /* do we have a valid "last" value? */
@@ -81,7 +76,7 @@ typedef struct SeqTableData
8176

8277
typedef SeqTableData *SeqTable;
8378

84-
static SeqTable seqtab = NULL; /* Head of list of SeqTable items */
79+
static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
8580

8681
/*
8782
* last_used_seq is updated by nextval() to point to the last used
@@ -92,6 +87,7 @@ static SeqTableData *last_used_seq = NULL;
9287
static void fill_seq_with_data(Relation rel, HeapTuple tuple);
9388
static int64 nextval_internal(Oid relid);
9489
static Relation open_share_lock(SeqTable seq);
90+
static void create_seq_hashtable(void);
9591
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
9692
static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
9793
Buffer *buf, HeapTuple seqtuple);
@@ -998,6 +994,23 @@ open_share_lock(SeqTable seq)
998994
return relation_open(seq->relid, NoLock);
999995
}
1000996

997+
/*
998+
* Creates the hash table for storing sequence data
999+
*/
1000+
static void
1001+
create_seq_hashtable(void)
1002+
{
1003+
HASHCTL ctl;
1004+
1005+
memset(&ctl, 0, sizeof(ctl));
1006+
ctl.keysize = sizeof(Oid);
1007+
ctl.entrysize = sizeof(SeqTableData);
1008+
ctl.hash = oid_hash;
1009+
1010+
seqhashtab = hash_create("Sequence values", 16, &ctl,
1011+
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
1012+
}
1013+
10011014
/*
10021015
* Given a relation OID, open and lock the sequence. p_elm and p_rel are
10031016
* output parameters.
@@ -1007,39 +1020,28 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
10071020
{
10081021
SeqTable elm;
10091022
Relation seqrel;
1023+
bool found;
10101024

1011-
/* Look to see if we already have a seqtable entry for relation */
1012-
for (elm = seqtab; elm != NULL; elm = elm->next)
1013-
{
1014-
if (elm->relid == relid)
1015-
break;
1016-
}
1025+
if (seqhashtab == NULL)
1026+
create_seq_hashtable();
1027+
1028+
elm = (SeqTable) hash_search(seqhashtab, &relid, HASH_ENTER, &found);
10171029

10181030
/*
1019-
* Allocate new seqtable entry if we didn't find one.
1031+
* Initalize the new hash table entry if it did not exist already.
10201032
*
1021-
* NOTE: seqtable entries remain in the list for the life of a backend. If
1022-
* the sequence itself is deleted then the entry becomes wasted memory,
1023-
* but it's small enough that this should not matter.
1033+
* NOTE: seqtable entries are stored for the life of a backend (unless
1034+
* explictly discarded with DISCARD). If the sequence itself is deleted
1035+
* then the entry becomes wasted memory, but it's small enough that this
1036+
* should not matter.
10241037
*/
1025-
if (elm == NULL)
1038+
if (!found)
10261039
{
1027-
/*
1028-
* Time to make a new seqtable entry. These entries live as long as
1029-
* the backend does, so we use plain malloc for them.
1030-
*/
1031-
elm = (SeqTable) malloc(sizeof(SeqTableData));
1032-
if (elm == NULL)
1033-
ereport(ERROR,
1034-
(errcode(ERRCODE_OUT_OF_MEMORY),
1035-
errmsg("out of memory")));
1036-
elm->relid = relid;
1040+
/* relid already filled in */
10371041
elm->filenode = InvalidOid;
10381042
elm->lxid = InvalidLocalTransactionId;
10391043
elm->last_valid = false;
10401044
elm->last = elm->cached = elm->increment = 0;
1041-
elm->next = seqtab;
1042-
seqtab = elm;
10431045
}
10441046

10451047
/*
@@ -1609,13 +1611,10 @@ seq_redo(XLogRecPtr lsn, XLogRecord *record)
16091611
void
16101612
ResetSequenceCaches(void)
16111613
{
1612-
SeqTableData *next;
1613-
1614-
while (seqtab != NULL)
1614+
if (seqhashtab)
16151615
{
1616-
next = seqtab->next;
1617-
free(seqtab);
1618-
seqtab = next;
1616+
hash_destroy(seqhashtab);
1617+
seqhashtab = NULL;
16191618
}
16201619

16211620
last_used_seq = NULL;

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