mirror of
https://github.com/postgres/postgres.git
synced 2025-04-20 00:42:27 +03:00
amcheck: Optimize speed of checking for unique constraint violation
Currently, when amcheck validates a unique constraint, it visits the heap for each index tuple. This commit implements skipping keys, which have only one non-dedeuplicated index tuple (quite common case for unique indexes). That gives substantial economy on index checking time. Reported-by: Noah Misch Discussion: https://postgr.es/m/20240325020323.fd.nmisch%40google.com Author: Alexander Korotkov, Pavel Borisov
This commit is contained in:
parent
b181062aa5
commit
cdd6ab9d1f
@ -1433,6 +1433,13 @@ bt_target_page_check(BtreeCheckState *state)
|
|||||||
bool lowersizelimit;
|
bool lowersizelimit;
|
||||||
ItemPointer scantid;
|
ItemPointer scantid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* True if we already called bt_entry_unique_check() for the current
|
||||||
|
* item. This helps to avoid visiting the heap for keys, which are
|
||||||
|
* anyway presented only once and can't comprise a unique violation.
|
||||||
|
*/
|
||||||
|
bool unique_checked = false;
|
||||||
|
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
itemid = PageGetItemIdCareful(state, state->targetblock,
|
itemid = PageGetItemIdCareful(state, state->targetblock,
|
||||||
@ -1775,12 +1782,18 @@ bt_target_page_check(BtreeCheckState *state)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If the index is unique verify entries uniqueness by checking the
|
* If the index is unique verify entries uniqueness by checking the
|
||||||
* heap tuples visibility.
|
* heap tuples visibility. Immediately check posting tuples and
|
||||||
|
* tuples with repeated keys. Postpone check for keys, which have the
|
||||||
|
* first appearance.
|
||||||
*/
|
*/
|
||||||
if (state->checkunique && state->indexinfo->ii_Unique &&
|
if (state->checkunique && state->indexinfo->ii_Unique &&
|
||||||
P_ISLEAF(topaque) && !skey->anynullkeys)
|
P_ISLEAF(topaque) && !skey->anynullkeys &&
|
||||||
|
(BTreeTupleIsPosting(itup) || ItemPointerIsValid(lVis.tid)))
|
||||||
|
{
|
||||||
bt_entry_unique_check(state, itup, state->targetblock, offset,
|
bt_entry_unique_check(state, itup, state->targetblock, offset,
|
||||||
&lVis);
|
&lVis);
|
||||||
|
unique_checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (state->checkunique && state->indexinfo->ii_Unique &&
|
if (state->checkunique && state->indexinfo->ii_Unique &&
|
||||||
P_ISLEAF(topaque) && OffsetNumberNext(offset) <= max)
|
P_ISLEAF(topaque) && OffsetNumberNext(offset) <= max)
|
||||||
@ -1799,6 +1812,9 @@ bt_target_page_check(BtreeCheckState *state)
|
|||||||
* data (whole index tuple or last posting in index tuple). Key
|
* data (whole index tuple or last posting in index tuple). Key
|
||||||
* containing null value does not violate unique constraint and
|
* containing null value does not violate unique constraint and
|
||||||
* treated as different to any other key.
|
* treated as different to any other key.
|
||||||
|
*
|
||||||
|
* If the next key is the same as the previous one, do the
|
||||||
|
* bt_entry_unique_check() call if it was postponed.
|
||||||
*/
|
*/
|
||||||
if (_bt_compare(state->rel, skey, state->target,
|
if (_bt_compare(state->rel, skey, state->target,
|
||||||
OffsetNumberNext(offset)) != 0 || skey->anynullkeys)
|
OffsetNumberNext(offset)) != 0 || skey->anynullkeys)
|
||||||
@ -1808,6 +1824,11 @@ bt_target_page_check(BtreeCheckState *state)
|
|||||||
lVis.postingIndex = -1;
|
lVis.postingIndex = -1;
|
||||||
lVis.tid = NULL;
|
lVis.tid = NULL;
|
||||||
}
|
}
|
||||||
|
else if (!unique_checked)
|
||||||
|
{
|
||||||
|
bt_entry_unique_check(state, itup, state->targetblock, offset,
|
||||||
|
&lVis);
|
||||||
|
}
|
||||||
skey->scantid = scantid; /* Restore saved scan key state */
|
skey->scantid = scantid; /* Restore saved scan key state */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1890,10 +1911,19 @@ bt_target_page_check(BtreeCheckState *state)
|
|||||||
rightkey->scantid = NULL;
|
rightkey->scantid = NULL;
|
||||||
|
|
||||||
/* The first key on the next page is the same */
|
/* The first key on the next page is the same */
|
||||||
if (_bt_compare(state->rel, rightkey, state->target, max) == 0 && !rightkey->anynullkeys)
|
if (_bt_compare(state->rel, rightkey, state->target, max) == 0 &&
|
||||||
|
!rightkey->anynullkeys)
|
||||||
{
|
{
|
||||||
Page rightpage;
|
Page rightpage;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do the bt_entry_unique_check() call if it was
|
||||||
|
* postponed.
|
||||||
|
*/
|
||||||
|
if (!unique_checked)
|
||||||
|
bt_entry_unique_check(state, itup, state->targetblock,
|
||||||
|
offset, &lVis);
|
||||||
|
|
||||||
elog(DEBUG2, "cross page equal keys");
|
elog(DEBUG2, "cross page equal keys");
|
||||||
rightpage = palloc_btree_page(state,
|
rightpage = palloc_btree_page(state,
|
||||||
rightblock_number);
|
rightblock_number);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user