diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2010-10-17 20:52:32 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2010-10-17 20:52:32 -0400 |
commit | 48c7d9f6ff99714495b7d6d2ebc44fbbe992cc8f (patch) | |
tree | 73082c19da244fedf1bfc81d5c5eb2a1f3eddd9e /src/backend/access/gin/ginutil.c | |
parent | cd0e8253216907982fe369b91f6d788d699b6c47 (diff) |
Improve GIN indexscan cost estimation.
The better estimate requires more statistics than we previously stored:
in particular, counts of "entry" versus "data" pages within the index,
as well as knowledge of the number of distinct key values. We collect
this information during initial index build and update it during VACUUM,
storing the info in new fields on the index metapage. No initdb is
required because these fields will read as zeroes in a pre-existing
index, and the new gincostestimate code is coded to behave (reasonably)
sanely if they are zeroes.
Teodor Sigaev, reviewed by Jan Urbanski, Tom Lane, and Itagaki Takahiro.
Diffstat (limited to 'src/backend/access/gin/ginutil.c')
-rw-r--r-- | src/backend/access/gin/ginutil.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index c128e5b3309..52bca8cee3c 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -13,10 +13,12 @@ */ #include "postgres.h" + #include "access/genam.h" #include "access/gin.h" #include "access/reloptions.h" #include "catalog/pg_type.h" +#include "miscadmin.h" #include "storage/bufmgr.h" #include "storage/freespace.h" #include "storage/indexfsm.h" @@ -227,6 +229,10 @@ GinInitMetabuffer(Buffer b) metadata->tailFreeSize = 0; metadata->nPendingPages = 0; metadata->nPendingHeapTuples = 0; + metadata->nTotalPages = 0; + metadata->nEntryPages = 0; + metadata->nDataPages = 0; + metadata->nEntries = 0; } int @@ -354,3 +360,82 @@ ginoptions(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(rdopts); } + +/* + * Fetch index's statistical data into *stats + * + * Note: in the result, nPendingPages can be trusted to be up-to-date, + * but the other fields are as of the last VACUUM. + */ +void +ginGetStats(Relation index, GinStatsData *stats) +{ + Buffer metabuffer; + Page metapage; + GinMetaPageData *metadata; + + metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO); + LockBuffer(metabuffer, GIN_SHARE); + metapage = BufferGetPage(metabuffer); + metadata = GinPageGetMeta(metapage); + + stats->nPendingPages = metadata->nPendingPages; + stats->nTotalPages = metadata->nTotalPages; + stats->nEntryPages = metadata->nEntryPages; + stats->nDataPages = metadata->nDataPages; + stats->nEntries = metadata->nEntries; + + UnlockReleaseBuffer(metabuffer); +} + +/* + * Write the given statistics to the index's metapage + * + * Note: nPendingPages is *not* copied over + */ +void +ginUpdateStats(Relation index, const GinStatsData *stats) +{ + Buffer metabuffer; + Page metapage; + GinMetaPageData *metadata; + + metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO); + LockBuffer(metabuffer, GIN_EXCLUSIVE); + metapage = BufferGetPage(metabuffer); + metadata = GinPageGetMeta(metapage); + + START_CRIT_SECTION(); + + metadata->nTotalPages = stats->nTotalPages; + metadata->nEntryPages = stats->nEntryPages; + metadata->nDataPages = stats->nDataPages; + metadata->nEntries = stats->nEntries; + + MarkBufferDirty(metabuffer); + + if (!index->rd_istemp) + { + XLogRecPtr recptr; + ginxlogUpdateMeta data; + XLogRecData rdata; + + data.node = index->rd_node; + data.ntuples = 0; + data.newRightlink = data.prevTail = InvalidBlockNumber; + memcpy(&data.metadata, metadata, sizeof(GinMetaPageData)); + + rdata.buffer = InvalidBuffer; + rdata.data = (char *) &data; + rdata.len = sizeof(ginxlogUpdateMeta); + rdata.next = NULL; + + recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE, &rdata); + PageSetLSN(metapage, recptr); + PageSetTLI(metapage, ThisTimeLineID); + } + + UnlockReleaseBuffer(metabuffer); + + END_CRIT_SECTION(); +} |