提交 d1996ed5 编写于 作者: H Heikki Linnakangas

Change the way parent pages are tracked during buffered GiST build.

We used to mimic the way a stack is constructed when descending the tree
during normal GiST inserts, but that was quite complicated during a buffered
build. It was also wrong: in GiST, the left-to-right relationships on
different levels might not match each other, so that when you know the
parent of a child page, you won't necessarily find the parent of the page to
the right of the child page by following the rightlinks at the parent level.
This sometimes led to "could not re-find parent" errors while building a
GiST index.

We now use a simple hash table to track the parent of every internal page.
Whenever a page is split, and downlinks are moved from one page to another,
we update the hash table accordingly. This is also better for performance
than the old method, as we never need to move right to re-find the parent
page, which could take a significant amount of time for buffers that were
created much earlier in the index build.
上级 be02b168
此差异已折叠。
......@@ -107,16 +107,7 @@ gistInitBuildBuffers(int pagesPerBuffer, int levelStep, int maxLevel)
sizeof(GISTNodeBuffer *));
gfbb->loadedBuffersCount = 0;
/*
* Root path item of the tree. Updated on each root node split.
*/
gfbb->rootitem = (GISTBufferingInsertStack *) MemoryContextAlloc(
gfbb->context, sizeof(GISTBufferingInsertStack));
gfbb->rootitem->parent = NULL;
gfbb->rootitem->blkno = GIST_ROOT_BLKNO;
gfbb->rootitem->downlinkoffnum = InvalidOffsetNumber;
gfbb->rootitem->level = maxLevel;
gfbb->rootitem->refCount = 1;
gfbb->rootlevel = maxLevel;
return gfbb;
}
......@@ -127,9 +118,7 @@ gistInitBuildBuffers(int pagesPerBuffer, int levelStep, int maxLevel)
*/
GISTNodeBuffer *
gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
BlockNumber nodeBlocknum,
OffsetNumber downlinkoffnum,
GISTBufferingInsertStack *parent)
BlockNumber nodeBlocknum, int level)
{
GISTNodeBuffer *nodeBuffer;
bool found;
......@@ -144,8 +133,6 @@ gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
/*
* Node buffer wasn't found. Initialize the new buffer as empty.
*/
GISTBufferingInsertStack *path;
int level;
MemoryContext oldcxt = MemoryContextSwitchTo(gfbb->context);
/* nodeBuffer->nodeBlocknum is the hash key and was filled in already */
......@@ -153,33 +140,12 @@ gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
nodeBuffer->pageBlocknum = InvalidBlockNumber;
nodeBuffer->pageBuffer = NULL;
nodeBuffer->queuedForEmptying = false;
/*
* Create a path stack for the page.
*/
if (nodeBlocknum != GIST_ROOT_BLKNO)
{
path = (GISTBufferingInsertStack *) palloc(
sizeof(GISTBufferingInsertStack));
path->parent = parent;
path->blkno = nodeBlocknum;
path->downlinkoffnum = downlinkoffnum;
path->level = parent->level - 1;
path->refCount = 0; /* initially unreferenced */
parent->refCount++; /* this path references its parent */
Assert(path->level > 0);
}
else
path = gfbb->rootitem;
nodeBuffer->path = path;
path->refCount++;
nodeBuffer->level = level;
/*
* Add this buffer to the list of buffers on this level. Enlarge
* buffersOnLevels array if needed.
*/
level = path->level;
if (level >= gfbb->buffersOnLevelsLen)
{
int i;
......@@ -210,20 +176,6 @@ gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
MemoryContextSwitchTo(oldcxt);
}
else
{
if (parent != nodeBuffer->path->parent)
{
/*
* A different parent path item was provided than we've
* remembered. We trust caller to provide more correct parent than
* we have. Previous parent may be outdated by page split.
*/
gistDecreasePathRefcount(nodeBuffer->path->parent);
nodeBuffer->path->parent = parent;
parent->refCount++;
}
}
return nodeBuffer;
}
......@@ -585,7 +537,7 @@ typedef struct
*/
void
gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
Relation r, GISTBufferingInsertStack *path,
Relation r, int level,
Buffer buffer, List *splitinfo)
{
RelocationBufferInfo *relocationBuffersInfos;
......@@ -601,7 +553,7 @@ gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
ListCell *lc;
/* If the splitted page doesn't have buffers, we have nothing to do. */
if (!LEVEL_HAS_BUFFERS(path->level, gfbb))
if (!LEVEL_HAS_BUFFERS(level, gfbb))
return;
/*
......@@ -660,14 +612,11 @@ gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb, GISTSTATE *giststate,
/*
* Create a node buffer for the page. The leftmost half is on the same
* block as the old page before split, so for the leftmost half this
* will return the original buffer, which was emptied earlier in this
* function.
* will return the original buffer. The tuples on the original buffer
* were relinked to the temporary buffer, so the original one is now
* empty.
*/
newNodeBuffer = gistGetNodeBuffer(gfbb,
giststate,
BufferGetBlockNumber(si->buf),
path->downlinkoffnum,
path->parent);
newNodeBuffer = gistGetNodeBuffer(gfbb, giststate, BufferGetBlockNumber(si->buf), level);
relocationBuffersInfos[i].nodeBuffer = newNodeBuffer;
relocationBuffersInfos[i].splitinfo = si;
......
......@@ -329,7 +329,7 @@ typedef struct
/* is this a temporary copy, not in the hash table? */
bool isTemp;
struct GISTBufferingInsertStack *path;
int level; /* 0 == leaf */
} GISTNodeBuffer;
/*
......@@ -338,7 +338,7 @@ typedef struct
*/
#define LEVEL_HAS_BUFFERS(nlevel, gfbb) \
((nlevel) != 0 && (nlevel) % (gfbb)->levelStep == 0 && \
(nlevel) != (gfbb)->rootitem->level)
(nlevel) != (gfbb)->rootlevel)
/* Is specified buffer at least half-filled (should be queued for emptying)? */
#define BUFFER_HALF_FILLED(nodeBuffer, gfbb) \
......@@ -352,26 +352,6 @@ typedef struct
#define BUFFER_OVERFLOWED(nodeBuffer, gfbb) \
((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer)
/*
* Extended GISTInsertStack for buffering GiST index build.
*/
typedef struct GISTBufferingInsertStack
{
/* current page */
BlockNumber blkno;
/* offset of the downlink in the parent page, that points to this page */
OffsetNumber downlinkoffnum;
/* pointer to parent */
struct GISTBufferingInsertStack *parent;
int refCount;
/* level number */
int level;
} GISTBufferingInsertStack;
/*
* Data structure with general information about build buffers.
*/
......@@ -416,8 +396,8 @@ typedef struct GISTBuildBuffers
int loadedBuffersCount; /* # of entries in loadedBuffers */
int loadedBuffersLen; /* allocated size of loadedBuffers */
/* A path item that points to the current root node */
GISTBufferingInsertStack *rootitem;
/* Level of the current root node (= height of the index tree - 1) */
int rootlevel;
} GISTBuildBuffers;
/*
......@@ -551,15 +531,13 @@ extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
/* gistbuild.c */
extern Datum gistbuild(PG_FUNCTION_ARGS);
extern void gistValidateBufferingOption(char *value);
extern void gistDecreasePathRefcount(GISTBufferingInsertStack *path);
/* gistbuildbuffers.c */
extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int levelStep,
int maxLevel);
extern GISTNodeBuffer *gistGetNodeBuffer(GISTBuildBuffers *gfbb,
GISTSTATE *giststate,
BlockNumber blkno, OffsetNumber downlinkoffnum,
GISTBufferingInsertStack *parent);
BlockNumber blkno, int level);
extern void gistPushItupToNodeBuffer(GISTBuildBuffers *gfbb,
GISTNodeBuffer *nodeBuffer, IndexTuple item);
extern bool gistPopItupFromNodeBuffer(GISTBuildBuffers *gfbb,
......@@ -567,7 +545,7 @@ extern bool gistPopItupFromNodeBuffer(GISTBuildBuffers *gfbb,
extern void gistFreeBuildBuffers(GISTBuildBuffers *gfbb);
extern void gistRelocateBuildBuffersOnSplit(GISTBuildBuffers *gfbb,
GISTSTATE *giststate, Relation r,
GISTBufferingInsertStack *path, Buffer buffer,
int level, Buffer buffer,
List *splitinfo);
extern void gistUnloadNodeBuffers(GISTBuildBuffers *gfbb);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册