Suppress useless searches for unused line pointers in PageAddItem. To do

this, add a 16-bit "flags" field to page headers by stealing some bits from
pd_tli.  We use one flag bit as a hint to indicate whether there are any
unused line pointers; the remaining 15 are available for future use.

This is a cut-down form of an idea proposed by Hiroki Kataoka in July 2005.
At the time it was rejected because the original patch increased the size of
page headers and it wasn't clear that the benefit outweighed the distributed
cost.  The flag-bit approach gets most of the benefit without requiring an
increase in the page header size.

Heikki Linnakangas and Tom Lane
This commit is contained in:
Tom Lane 2007-03-02 00:48:44 +00:00
parent 44f72c6e9e
commit fb276438b6
4 changed files with 83 additions and 26 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/storage.sgml,v 1.14 2007/01/31 20:56:19 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/storage.sgml,v 1.15 2007/03/02 00:48:44 tgl Exp $ -->
<chapter id="storage">
@ -427,8 +427,8 @@ data. Empty in ordinary tables.</entry>
The first 20 bytes of each page consists of a page header
(PageHeaderData). Its format is detailed in <xref
linkend="pageheaderdata-table">. The first two fields track the most
recent WAL entry related to this page. They are followed by three 2-byte
integer fields
recent WAL entry related to this page. Next is a 2-byte field
containing flag bits. This is followed by three 2-byte integer fields
(<structfield>pd_lower</structfield>, <structfield>pd_upper</structfield>,
and <structfield>pd_special</structfield>). These contain byte offsets
from the page start to the start
@ -437,12 +437,13 @@ data. Empty in ordinary tables.</entry>
The last 2 bytes of the page header,
<structfield>pd_pagesize_version</structfield>, store both the page size
and a version indicator. Beginning with
<productname>PostgreSQL</productname> 8.1 the version number is 3;
<productname>PostgreSQL</productname> 8.3 the version number is 4;
<productname>PostgreSQL</productname> 8.1 and 8.2 used version number 3;
<productname>PostgreSQL</productname> 8.0 used version number 2;
<productname>PostgreSQL</productname> 7.3 and 7.4 used version number 1;
prior releases used version number 0.
(The basic page layout and header format has not changed in these versions,
but the layout of heap row headers has.) The page size
(The basic page layout and header format has not changed in most of these
versions, but the layout of heap row headers has.) The page size
is basically only present as a cross-check; there is no support for having
more than one page size in an installation.
@ -470,9 +471,15 @@ data. Empty in ordinary tables.</entry>
</row>
<row>
<entry>pd_tli</entry>
<entry>TimeLineID</entry>
<entry>4 bytes</entry>
<entry>TLI of last change</entry>
<entry>uint16</entry>
<entry>2 bytes</entry>
<entry>TimeLineID of last change (only its lowest 16 bits)</entry>
</row>
<row>
<entry>pd_flags</entry>
<entry>uint16</entry>
<entry>2 bytes</entry>
<entry>Flag bits</entry>
</row>
<row>
<entry>pd_lower</entry>

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/page/bufpage.c,v 1.71 2007/02/21 20:02:17 momjian Exp $
* $PostgreSQL: pgsql/src/backend/storage/page/bufpage.c,v 1.72 2007/03/02 00:48:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -39,6 +39,7 @@ PageInit(Page page, Size pageSize, Size specialSize)
/* Make sure all fields of page are zero, as well as unused space */
MemSet(p, 0, pageSize);
/* p->pd_flags = 0; done by above MemSet */
p->pd_lower = SizeOfPageHeaderData;
p->pd_upper = pageSize - specialSize;
p->pd_special = pageSize - specialSize;
@ -73,6 +74,7 @@ PageHeaderIsValid(PageHeader page)
/* Check normal case */
if (PageGetPageSize(page) == BLCKSZ &&
PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION &&
(page->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
page->pd_lower >= SizeOfPageHeaderData &&
page->pd_lower <= page->pd_upper &&
page->pd_upper <= page->pd_special &&
@ -165,14 +167,27 @@ PageAddItem(Page page,
else
{
/* offsetNumber was not passed in, so find a free slot */
/* look for "recyclable" (unused & deallocated) ItemId */
for (offsetNumber = 1; offsetNumber < limit; offsetNumber++)
{
itemId = PageGetItemId(phdr, offsetNumber);
if (!ItemIdIsUsed(itemId) && ItemIdGetLength(itemId) == 0)
break;
}
/* if no free slot, we'll put it at limit (1st open slot) */
if (PageHasFreeLinePointers(phdr))
{
/* look for "recyclable" (unused & deallocated) ItemId */
for (offsetNumber = 1; offsetNumber < limit; offsetNumber++)
{
itemId = PageGetItemId(phdr, offsetNumber);
if (!ItemIdIsUsed(itemId) && ItemIdGetLength(itemId) == 0)
break;
}
if (offsetNumber >= limit)
{
/* the hint is wrong, so reset it */
PageClearHasFreeLinePointers(phdr);
}
}
else
{
/* don't bother searching if hint says there's no free slot */
offsetNumber = limit;
}
}
if (offsetNumber > limit)
@ -413,13 +428,19 @@ PageRepairFragmentation(Page page, OffsetNumber *unused)
pfree(itemidbase);
}
/* Set hint bit for PageAddItem */
if (nused < nline)
PageSetHasFreeLinePointers(page);
else
PageClearHasFreeLinePointers(page);
return (nline - nused);
}
/*
* PageGetFreeSpace
* Returns the size of the free (allocatable) space on a page,
* deducted by the space needed for a new line pointer.
* reduced by the space needed for a new line pointer.
*/
Size
PageGetFreeSpace(Page page)

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.388 2007/02/20 17:32:17 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.389 2007/03/02 00:48:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200702202
#define CATALOG_VERSION_NO 200703011
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/bufpage.h,v 1.71 2007/02/21 20:02:17 momjian Exp $
* $PostgreSQL: pgsql/src/include/storage/bufpage.h,v 1.72 2007/03/02 00:48:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -90,6 +90,7 @@ typedef uint16 LocationIndex;
*
* pd_lsn - identifies xlog record for last change to this page.
* pd_tli - ditto.
* pd_flags - flag bits.
* pd_lower - offset to start of free space.
* pd_upper - offset to end of free space.
* pd_special - offset to start of special space.
@ -98,8 +99,9 @@ typedef uint16 LocationIndex;
* The LSN is used by the buffer manager to enforce the basic rule of WAL:
* "thou shalt write xlog before data". A dirty buffer cannot be dumped
* to disk until xlog has been flushed at least as far as the page's LSN.
* We also store the TLI for identification purposes (it is not clear that
* this is actually necessary, but it seems like a good idea).
* We also store the 16 least significant bits of the TLI for identification
* purposes (it is not clear that this is actually necessary, but it seems
* like a good idea).
*
* The page version number and page size are packed together into a single
* uint16 field. This is for historical reasons: before PostgreSQL 7.3,
@ -119,7 +121,9 @@ typedef struct PageHeaderData
/* XXX LSN is member of *any* block, not only page-organized ones */
XLogRecPtr pd_lsn; /* LSN: next byte after last byte of xlog
* record for last change to this page */
TimeLineID pd_tli; /* TLI of last change */
uint16 pd_tli; /* least significant bits of the TimeLineID
* containing the LSN */
uint16 pd_flags; /* flag bits, see below */
LocationIndex pd_lower; /* offset to start of free space */
LocationIndex pd_upper; /* offset to end of free space */
LocationIndex pd_special; /* offset to start of special space */
@ -129,12 +133,25 @@ typedef struct PageHeaderData
typedef PageHeaderData *PageHeader;
/*
* pd_flags contains the following flag bits. Undefined bits are initialized
* to zero and may be used in the future.
*
* PD_HAS_FREE_LINES is set if there are any not-LP_USED line pointers before
* pd_lower. This should be considered a hint rather than the truth, since
* changes to it are not WAL-logged.
*/
#define PD_HAS_FREE_LINES 0x0001 /* are there any unused line pointers? */
#define PD_VALID_FLAG_BITS 0x0001 /* OR of all valid pd_flags bits */
/*
* Page layout version number 0 is for pre-7.3 Postgres releases.
* Releases 7.3 and 7.4 use 1, denoting a new HeapTupleHeader layout.
* Release 8.0 uses 2; it changed the HeapTupleHeader layout again.
* Release 8.1 uses 3; it redefined HeapTupleHeader infomask bits.
* Release 8.3 uses 4; it changed the HeapTupleHeader layout again.
* Release 8.3 uses 4; it changed the HeapTupleHeader layout again, and
* added the pd_flags field (by stealing some bits from pd_tli).
*/
#define PG_PAGE_LAYOUT_VERSION 4
@ -299,15 +316,27 @@ typedef PageHeaderData *PageHeader;
((((PageHeader) (page))->pd_lower - SizeOfPageHeaderData) \
/ sizeof(ItemIdData)))
/*
* Additional macros for access to page headers
*/
#define PageGetLSN(page) \
(((PageHeader) (page))->pd_lsn)
#define PageSetLSN(page, lsn) \
(((PageHeader) (page))->pd_lsn = (lsn))
/* NOTE: only the 16 least significant bits are stored */
#define PageGetTLI(page) \
(((PageHeader) (page))->pd_tli)
#define PageSetTLI(page, tli) \
(((PageHeader) (page))->pd_tli = (tli))
(((PageHeader) (page))->pd_tli = (uint16) (tli))
#define PageHasFreeLinePointers(page) \
(((PageHeader) (page))->pd_flags & PD_HAS_FREE_LINES)
#define PageSetHasFreeLinePointers(page) \
(((PageHeader) (page))->pd_flags |= PD_HAS_FREE_LINES)
#define PageClearHasFreeLinePointers(page) \
(((PageHeader) (page))->pd_flags &= ~PD_HAS_FREE_LINES)
/* ----------------------------------------------------------------
* extern declarations