From 7d0bd28f75cf2b26706fe5cc8473aa01950286cb Mon Sep 17 00:00:00 2001 From: "houzj.fnst" Date: Mon, 7 Jun 2021 18:57:14 +0800 Subject: [PATCH] cache-bound-offset_v3_modify --- src/backend/executor/execPartition.c | 149 ++++++++++++++++++++++----- src/backend/partitioning/partdesc.c | 2 + src/include/partitioning/partdesc.h | 3 + 3 files changed, 130 insertions(+), 24 deletions(-) diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 606c920b06..122d330a1d 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -150,9 +150,15 @@ typedef struct PartitionDispatchData PartitionDesc partdesc; TupleTableSlot *tupslot; AttrMap *tupmap; + + short ntupinserts; + short npartchanges; + bool force; + bool check_cached; int indexes[FLEXIBLE_ARRAY_MEMBER]; } PartitionDispatchData; +#define RECHECK_BOUND_CACHE_THRESHOLD 1000 static ResultRelInfo *ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, @@ -176,7 +182,7 @@ static void FormPartitionKeyDatum(PartitionDispatch pd, Datum *values, bool *isnull); static int get_partition_for_tuple(PartitionDispatch pd, Datum *values, - bool *isnull); + bool *isnull, bool check_cached); static char *ExecBuildSlotPartitionKeyDescription(Relation rel, Datum *values, bool *isnull, @@ -305,12 +311,23 @@ ExecFindPartition(ModifyTableState *mtstate, ecxt->ecxt_scantuple = slot; FormPartitionKeyDatum(dispatch, slot, estate, values, isnull); + if (dispatch->ntupinserts > RECHECK_BOUND_CACHE_THRESHOLD && + !dispatch->force) + { + if (dispatch->npartchanges == 0) + dispatch->check_cached = true; + else + dispatch->check_cached = ((dispatch->ntupinserts/dispatch->npartchanges) > 1); + dispatch->ntupinserts = dispatch->npartchanges = 0; + } + /* * If this partitioned table has no partitions or no partition for * these values, error out. */ if (partdesc->nparts == 0 || - (partidx = get_partition_for_tuple(dispatch, values, isnull)) < 0) + (partidx = get_partition_for_tuple(dispatch, values, isnull, + dispatch->check_cached)) < 0) { char *val_desc; @@ -1026,6 +1043,12 @@ ExecInitPartitionDispatchInfo(EState *estate, pd->key = RelationGetPartitionKey(rel); pd->keystate = NIL; pd->partdesc = partdesc; + pd->ntupinserts = 0; + pd->npartchanges = 0; + partdesc->cached_bound_offset = -1; + + pd->check_cached = false; + if (parent_pd != NULL) { TupleDesc tupdesc = RelationGetDescr(rel); @@ -1236,11 +1259,16 @@ FormPartitionKeyDatum(PartitionDispatch pd, * Finds partition of relation which accepts the partition key specified * in values and isnull * + * If check_cached is true, this short-circuits a full-blown search across all + * the bounds, instead checking the tuple against the bound whose offset is + * cached in pd->partdesc. + * * Return value is index of the partition (>= 0 and < partdesc->nparts) if one * found or -1 if none found. */ static int -get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull) +get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull, + bool check_cached) { int bound_offset; int part_index = -1; @@ -1248,10 +1276,18 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull) PartitionDesc partdesc = pd->partdesc; PartitionBoundInfo boundinfo = partdesc->boundinfo; + /* + * If the cached bound offset is valid, we check below if the that bound + * is satisfied by the new tuple. If it is, there's no need to perform a + * search across all bounds. + */ + int cached_off = partdesc->cached_bound_offset; + /* Route as appropriate based on partitioning strategy. */ switch (key->strategy) { case PARTITION_STRATEGY_HASH: + Assert(cached_off < 0); { uint64 rowHash; @@ -1265,6 +1301,7 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull) break; case PARTITION_STRATEGY_LIST: + pd->ntupinserts++; if (isnull[0]) { if (partition_bound_accepts_nulls(boundinfo)) @@ -1272,14 +1309,37 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull) } else { - bool equal = false; - - bound_offset = partition_list_bsearch(key->partsupfunc, - key->partcollation, - boundinfo, - values[0], &equal); - if (bound_offset >= 0 && equal) - part_index = boundinfo->indexes[bound_offset]; + if (cached_off >= 0 && check_cached) + { + Datum bound_datum = boundinfo->datums[cached_off][0]; + int32 cmpval; + + cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0], + key->partcollation[0], + bound_datum, + values[0])); + if (cmpval == 0) + part_index = boundinfo->indexes[cached_off]; + } + + if (part_index < 0) + { + bool equal = false; + + bound_offset = partition_list_bsearch(key->partsupfunc, + key->partcollation, + boundinfo, + values[0], &equal); + if (bound_offset >= 0 && equal) + { + part_index = boundinfo->indexes[bound_offset]; + if (cached_off != bound_offset) + { + pd->npartchanges++; + partdesc->cached_bound_offset = bound_offset; + } + } + } } break; @@ -1289,6 +1349,7 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull) range_partkey_has_null = false; int i; + pd->ntupinserts++; /* * No range includes NULL, so this will be accepted by the * default partition if there is one, and otherwise rejected. @@ -1304,20 +1365,60 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull) if (!range_partkey_has_null) { - bound_offset = partition_range_datum_bsearch(key->partsupfunc, - key->partcollation, - boundinfo, - key->partnatts, - values, - &equal); + if (cached_off >= 0 && check_cached) + { + Datum *bound_datums = boundinfo->datums[cached_off]; + PartitionRangeDatumKind *bound_kind = boundinfo->kind[cached_off]; + int32 cmpval; + + /* Check if the value is above the low bound */ + cmpval = partition_rbound_datum_cmp(key->partsupfunc, + key->partcollation, + bound_datums, + bound_kind, + values, + key->partnatts); + if (cmpval == 0) + part_index = boundinfo->indexes[cached_off + 1]; + else if (cmpval < 0 && cached_off + 1 < boundinfo->ndatums) + { + /* Check if the value is below the high bound */ + bound_datums = boundinfo->datums[cached_off + 1]; + bound_kind = boundinfo->kind[cached_off + 1]; + cmpval = partition_rbound_datum_cmp(key->partsupfunc, + key->partcollation, + bound_datums, + bound_kind, + values, + key->partnatts); + + if (cmpval > 0) + part_index = boundinfo->indexes[cached_off + 1]; + } + } - /* - * The bound at bound_offset is less than or equal to the - * tuple value, so the bound at offset+1 is the upper - * bound of the partition we're looking for, if there - * actually exists one. - */ - part_index = boundinfo->indexes[bound_offset + 1]; + if (part_index < 0) + { + bound_offset = partition_range_datum_bsearch(key->partsupfunc, + key->partcollation, + boundinfo, + key->partnatts, + values, + &equal); + + /* + * The bound at bound_offset is less than or equal to the + * tuple value, so the bound at offset+1 is the upper + * bound of the partition we're looking for, if there + * actually exists one. + */ + part_index = boundinfo->indexes[bound_offset + 1]; + if (cached_off != bound_offset) + { + pd->npartchanges++; + partdesc->cached_bound_offset = bound_offset; + } + } } } break; diff --git a/src/backend/partitioning/partdesc.c b/src/backend/partitioning/partdesc.c index 9a9d6a9643..e6ec01de71 100644 --- a/src/backend/partitioning/partdesc.c +++ b/src/backend/partitioning/partdesc.c @@ -285,6 +285,8 @@ RelationBuildPartitionDesc(Relation rel, bool omit_detached) MemoryContextAllocZero(new_pdcxt, sizeof(PartitionDescData)); partdesc->nparts = nparts; partdesc->detached_exist = detached_exist; + partdesc->cached_bound_offset = -1; + /* If there are no partitions, the rest of the partdesc can stay zero */ if (nparts > 0) { diff --git a/src/include/partitioning/partdesc.h b/src/include/partitioning/partdesc.h index 0792f48507..9df28ae14f 100644 --- a/src/include/partitioning/partdesc.h +++ b/src/include/partitioning/partdesc.h @@ -36,6 +36,9 @@ typedef struct PartitionDescData * the corresponding 'oids' element belongs to * a leaf partition or not */ PartitionBoundInfo boundinfo; /* collection of partition bounds */ + int cached_bound_offset; /* offset of the bound datum most + * recently chosen by + * get_partition_for_tuple() */ } PartitionDescData; -- 2.18.4