Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+35-36
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)