Commit 608195a3 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Introduce visibility map. The visibility map is a bitmap with one bit per

heap page, where a set bit indicates that all tuples on the page are
visible to all transactions, and the page therefore doesn't need
vacuuming. It is stored in a new relation fork.

Lazy vacuum uses the visibility map to skip pages that don't need
vacuuming. Vacuum is also responsible for setting the bits in the map.
In the future, this can hopefully be used to implement index-only-scans,
but we can't currently guarantee that the visibility map is always 100%
up-to-date.

In addition to the visibility map, there's a new PD_ALL_VISIBLE flag on
each heap page, also indicating that all tuples on the page are visible to
all transactions. It's important that this flag is kept up-to-date. It
is also used to skip visibility tests in sequential scans, which gives a
small performance gain on seqscans.
parent 44ff9096
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for access/heap # Makefile for access/heap
# #
# IDENTIFICATION # IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/access/heap/Makefile,v 1.18 2008/02/19 10:30:06 petere Exp $ # $PostgreSQL: pgsql/src/backend/access/heap/Makefile,v 1.19 2008/12/03 13:05:22 heikki Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -12,6 +12,6 @@ subdir = src/backend/access/heap ...@@ -12,6 +12,6 @@ subdir = src/backend/access/heap
top_builddir = ../../../.. top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global include $(top_builddir)/src/Makefile.global
OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk include $(top_srcdir)/src/backend/common.mk
This diff is collapsed.
This diff is collapsed.
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.64 2008/11/26 17:08:57 heikki Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.65 2008/12/03 13:05:22 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -377,6 +377,7 @@ CreateFakeRelcacheEntry(RelFileNode rnode) ...@@ -377,6 +377,7 @@ CreateFakeRelcacheEntry(RelFileNode rnode)
rel->rd_targblock = InvalidBlockNumber; rel->rd_targblock = InvalidBlockNumber;
rel->rd_fsm_nblocks = InvalidBlockNumber; rel->rd_fsm_nblocks = InvalidBlockNumber;
rel->rd_vm_nblocks = InvalidBlockNumber;
rel->rd_smgr = NULL; rel->rd_smgr = NULL;
return rel; return rel;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.79 2008/10/06 14:13:17 heikki Exp $ * $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.80 2008/12/03 13:05:22 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -54,7 +54,8 @@ ...@@ -54,7 +54,8 @@
*/ */
const char *forkNames[] = { const char *forkNames[] = {
"main", /* MAIN_FORKNUM */ "main", /* MAIN_FORKNUM */
"fsm" /* FSM_FORKNUM */ "fsm", /* FSM_FORKNUM */
"vm" /* VISIBILITYMAP_FORKNUM */
}; };
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/storage.c,v 1.1 2008/11/19 10:34:51 heikki Exp $ * $PostgreSQL: pgsql/src/backend/catalog/storage.c,v 1.2 2008/12/03 13:05:22 heikki Exp $
* *
* NOTES * NOTES
* Some of this code used to be in storage/smgr/smgr.c, and the * Some of this code used to be in storage/smgr/smgr.c, and the
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "postgres.h" #include "postgres.h"
#include "access/visibilitymap.h"
#include "access/xact.h" #include "access/xact.h"
#include "access/xlogutils.h" #include "access/xlogutils.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
...@@ -175,6 +176,7 @@ void ...@@ -175,6 +176,7 @@ void
RelationTruncate(Relation rel, BlockNumber nblocks) RelationTruncate(Relation rel, BlockNumber nblocks)
{ {
bool fsm; bool fsm;
bool vm;
/* Open it at the smgr level if not already done */ /* Open it at the smgr level if not already done */
RelationOpenSmgr(rel); RelationOpenSmgr(rel);
...@@ -187,6 +189,11 @@ RelationTruncate(Relation rel, BlockNumber nblocks) ...@@ -187,6 +189,11 @@ RelationTruncate(Relation rel, BlockNumber nblocks)
if (fsm) if (fsm)
FreeSpaceMapTruncateRel(rel, nblocks); FreeSpaceMapTruncateRel(rel, nblocks);
/* Truncate the visibility map too if it exists. */
vm = smgrexists(rel->rd_smgr, VISIBILITYMAP_FORKNUM);
if (vm)
visibilitymap_truncate(rel, nblocks);
/* /*
* We WAL-log the truncation before actually truncating, which * We WAL-log the truncation before actually truncating, which
* means trouble if the truncation fails. If we then crash, the WAL * means trouble if the truncation fails. If we then crash, the WAL
...@@ -217,12 +224,12 @@ RelationTruncate(Relation rel, BlockNumber nblocks) ...@@ -217,12 +224,12 @@ RelationTruncate(Relation rel, BlockNumber nblocks)
/* /*
* Flush, because otherwise the truncation of the main relation * Flush, because otherwise the truncation of the main relation
* might hit the disk before the WAL record of truncating the * might hit the disk before the WAL record, and the truncation of
* FSM is flushed. If we crashed during that window, we'd be * the FSM or visibility map. If we crashed during that window, we'd
* left with a truncated heap, but the FSM would still contain * be left with a truncated heap, but the FSM or visibility map would
* entries for the non-existent heap pages. * still contain entries for the non-existent heap pages.
*/ */
if (fsm) if (fsm || vm)
XLogFlush(lsn); XLogFlush(lsn);
} }
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.381 2008/11/19 10:34:51 heikki Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.382 2008/12/03 13:05:22 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "access/transam.h" #include "access/transam.h"
#include "access/visibilitymap.h"
#include "access/xact.h" #include "access/xact.h"
#include "access/xlog.h" #include "access/xlog.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
...@@ -3005,10 +3006,19 @@ move_chain_tuple(Relation rel, ...@@ -3005,10 +3006,19 @@ move_chain_tuple(Relation rel,
END_CRIT_SECTION(); END_CRIT_SECTION();
PageClearAllVisible(BufferGetPage(old_buf));
if (dst_buf != old_buf)
PageClearAllVisible(BufferGetPage(dst_buf));
LockBuffer(dst_buf, BUFFER_LOCK_UNLOCK); LockBuffer(dst_buf, BUFFER_LOCK_UNLOCK);
if (dst_buf != old_buf) if (dst_buf != old_buf)
LockBuffer(old_buf, BUFFER_LOCK_UNLOCK); LockBuffer(old_buf, BUFFER_LOCK_UNLOCK);
/* Clear the bits in the visibility map. */
visibilitymap_clear(rel, BufferGetBlockNumber(old_buf));
if (dst_buf != old_buf)
visibilitymap_clear(rel, BufferGetBlockNumber(dst_buf));
/* Create index entries for the moved tuple */ /* Create index entries for the moved tuple */
if (ec->resultRelInfo->ri_NumIndices > 0) if (ec->resultRelInfo->ri_NumIndices > 0)
{ {
...@@ -3107,6 +3117,23 @@ move_plain_tuple(Relation rel, ...@@ -3107,6 +3117,23 @@ move_plain_tuple(Relation rel,
END_CRIT_SECTION(); END_CRIT_SECTION();
/*
* Clear the visible-to-all hint bits on the page, and bits in the
* visibility map. Normally we'd release the locks on the heap pages
* before updating the visibility map, but doesn't really matter here
* because we're holding an AccessExclusiveLock on the relation anyway.
*/
if (PageIsAllVisible(dst_page))
{
PageClearAllVisible(dst_page);
visibilitymap_clear(rel, BufferGetBlockNumber(dst_buf));
}
if (PageIsAllVisible(old_page))
{
PageClearAllVisible(old_page);
visibilitymap_clear(rel, BufferGetBlockNumber(old_buf));
}
dst_vacpage->free = PageGetFreeSpaceWithFillFactor(rel, dst_page); dst_vacpage->free = PageGetFreeSpaceWithFillFactor(rel, dst_page);
LockBuffer(dst_buf, BUFFER_LOCK_UNLOCK); LockBuffer(dst_buf, BUFFER_LOCK_UNLOCK);
LockBuffer(old_buf, BUFFER_LOCK_UNLOCK); LockBuffer(old_buf, BUFFER_LOCK_UNLOCK);
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.111 2008/11/19 10:34:51 heikki Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.112 2008/12/03 13:05:22 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "access/transam.h" #include "access/transam.h"
#include "access/visibilitymap.h"
#include "catalog/storage.h" #include "catalog/storage.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "commands/vacuum.h" #include "commands/vacuum.h"
...@@ -88,6 +89,7 @@ typedef struct LVRelStats ...@@ -88,6 +89,7 @@ typedef struct LVRelStats
int max_dead_tuples; /* # slots allocated in array */ int max_dead_tuples; /* # slots allocated in array */
ItemPointer dead_tuples; /* array of ItemPointerData */ ItemPointer dead_tuples; /* array of ItemPointerData */
int num_index_scans; int num_index_scans;
bool scanned_all; /* have we scanned all pages (this far)? */
} LVRelStats; } LVRelStats;
...@@ -102,7 +104,7 @@ static BufferAccessStrategy vac_strategy; ...@@ -102,7 +104,7 @@ static BufferAccessStrategy vac_strategy;
/* non-export function prototypes */ /* non-export function prototypes */
static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
Relation *Irel, int nindexes); Relation *Irel, int nindexes, bool scan_all);
static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats); static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
static void lazy_vacuum_index(Relation indrel, static void lazy_vacuum_index(Relation indrel,
IndexBulkDeleteResult **stats, IndexBulkDeleteResult **stats,
...@@ -141,6 +143,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, ...@@ -141,6 +143,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
BlockNumber possibly_freeable; BlockNumber possibly_freeable;
PGRUsage ru0; PGRUsage ru0;
TimestampTz starttime = 0; TimestampTz starttime = 0;
bool scan_all;
pg_rusage_init(&ru0); pg_rusage_init(&ru0);
...@@ -161,13 +164,20 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, ...@@ -161,13 +164,20 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats)); vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
vacrelstats->num_index_scans = 0; vacrelstats->num_index_scans = 0;
vacrelstats->scanned_all = true;
/* Open all indexes of the relation */ /* Open all indexes of the relation */
vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel); vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel);
vacrelstats->hasindex = (nindexes > 0); vacrelstats->hasindex = (nindexes > 0);
/* Should we use the visibility map or scan all pages? */
if (vacstmt->freeze_min_age != -1)
scan_all = true;
else
scan_all = false;
/* Do the vacuuming */ /* Do the vacuuming */
lazy_scan_heap(onerel, vacrelstats, Irel, nindexes); lazy_scan_heap(onerel, vacrelstats, Irel, nindexes, scan_all);
/* Done with indexes */ /* Done with indexes */
vac_close_indexes(nindexes, Irel, NoLock); vac_close_indexes(nindexes, Irel, NoLock);
...@@ -186,10 +196,14 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, ...@@ -186,10 +196,14 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
/* Vacuum the Free Space Map */ /* Vacuum the Free Space Map */
FreeSpaceMapVacuum(onerel); FreeSpaceMapVacuum(onerel);
/* Update statistics in pg_class */ /*
* Update statistics in pg_class. We can only advance relfrozenxid if we
* didn't skip any pages.
*/
vac_update_relstats(onerel, vac_update_relstats(onerel,
vacrelstats->rel_pages, vacrelstats->rel_tuples, vacrelstats->rel_pages, vacrelstats->rel_tuples,
vacrelstats->hasindex, FreezeLimit); vacrelstats->hasindex,
vacrelstats->scanned_all ? FreezeLimit : InvalidOid);
/* report results to the stats collector, too */ /* report results to the stats collector, too */
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared, pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
...@@ -230,13 +244,14 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, ...@@ -230,13 +244,14 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
*/ */
static void static void
lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
Relation *Irel, int nindexes) Relation *Irel, int nindexes, bool scan_all)
{ {
BlockNumber nblocks, BlockNumber nblocks,
blkno; blkno;
HeapTupleData tuple; HeapTupleData tuple;
char *relname; char *relname;
BlockNumber empty_pages, BlockNumber empty_pages,
scanned_pages,
vacuumed_pages; vacuumed_pages;
double num_tuples, double num_tuples,
tups_vacuumed, tups_vacuumed,
...@@ -245,6 +260,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -245,6 +260,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
IndexBulkDeleteResult **indstats; IndexBulkDeleteResult **indstats;
int i; int i;
PGRUsage ru0; PGRUsage ru0;
Buffer vmbuffer = InvalidBuffer;
pg_rusage_init(&ru0); pg_rusage_init(&ru0);
...@@ -254,7 +270,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -254,7 +270,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
get_namespace_name(RelationGetNamespace(onerel)), get_namespace_name(RelationGetNamespace(onerel)),
relname))); relname)));
empty_pages = vacuumed_pages = 0; empty_pages = vacuumed_pages = scanned_pages = 0;
num_tuples = tups_vacuumed = nkeep = nunused = 0; num_tuples = tups_vacuumed = nkeep = nunused = 0;
indstats = (IndexBulkDeleteResult **) indstats = (IndexBulkDeleteResult **)
...@@ -278,9 +294,28 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -278,9 +294,28 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
OffsetNumber frozen[MaxOffsetNumber]; OffsetNumber frozen[MaxOffsetNumber];
int nfrozen; int nfrozen;
Size freespace; Size freespace;
bool all_visible_according_to_vm = false;
bool all_visible;
/*
* Skip pages that don't require vacuuming according to the
* visibility map.
*/
if (!scan_all)
{
all_visible_according_to_vm =
visibilitymap_test(onerel, blkno, &vmbuffer);
if (all_visible_according_to_vm)
{
vacrelstats->scanned_all = false;
continue;
}
}
vacuum_delay_point(); vacuum_delay_point();
scanned_pages++;
/* /*
* If we are close to overrunning the available space for dead-tuple * If we are close to overrunning the available space for dead-tuple
* TIDs, pause and do a cycle of vacuuming before we tackle this page. * TIDs, pause and do a cycle of vacuuming before we tackle this page.
...@@ -354,7 +389,26 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -354,7 +389,26 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
{ {
empty_pages++; empty_pages++;
freespace = PageGetHeapFreeSpace(page); freespace = PageGetHeapFreeSpace(page);
UnlockReleaseBuffer(buf);
if (!PageIsAllVisible(page))
{
SetBufferCommitInfoNeedsSave(buf);
PageSetAllVisible(page);
}
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
/* Update the visibility map */
if (!all_visible_according_to_vm)
{
visibilitymap_pin(onerel, blkno, &vmbuffer);
LockBuffer(buf, BUFFER_LOCK_SHARE);
if (PageIsAllVisible(page))
visibilitymap_set(onerel, blkno, PageGetLSN(page), &vmbuffer);
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
}
ReleaseBuffer(buf);
RecordPageWithFreeSpace(onerel, blkno, freespace); RecordPageWithFreeSpace(onerel, blkno, freespace);
continue; continue;
} }
...@@ -371,6 +425,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -371,6 +425,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
* Now scan the page to collect vacuumable items and check for tuples * Now scan the page to collect vacuumable items and check for tuples
* requiring freezing. * requiring freezing.
*/ */
all_visible = true;
nfrozen = 0; nfrozen = 0;
hastup = false; hastup = false;
prev_dead_count = vacrelstats->num_dead_tuples; prev_dead_count = vacrelstats->num_dead_tuples;
...@@ -408,6 +463,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -408,6 +463,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
if (ItemIdIsDead(itemid)) if (ItemIdIsDead(itemid))
{ {
lazy_record_dead_tuple(vacrelstats, &(tuple.t_self)); lazy_record_dead_tuple(vacrelstats, &(tuple.t_self));
all_visible = false;
continue; continue;
} }
...@@ -442,6 +498,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -442,6 +498,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
nkeep += 1; nkeep += 1;
else else
tupgone = true; /* we can delete the tuple */ tupgone = true; /* we can delete the tuple */
all_visible = false;
break; break;
case HEAPTUPLE_LIVE: case HEAPTUPLE_LIVE:
/* Tuple is good --- but let's do some validity checks */ /* Tuple is good --- but let's do some validity checks */
...@@ -449,6 +506,36 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -449,6 +506,36 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
!OidIsValid(HeapTupleGetOid(&tuple))) !OidIsValid(HeapTupleGetOid(&tuple)))
elog(WARNING, "relation \"%s\" TID %u/%u: OID is invalid", elog(WARNING, "relation \"%s\" TID %u/%u: OID is invalid",
relname, blkno, offnum); relname, blkno, offnum);
/*
* Is the tuple definitely visible to all transactions?
*
* NB: Like with per-tuple hint bits, we can't set the
* PD_ALL_VISIBLE flag if the inserter committed
* asynchronously. See SetHintBits for more info. Check
* that the HEAP_XMIN_COMMITTED hint bit is set because of
* that.
*/
if (all_visible)
{
TransactionId xmin;
if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED))
{
all_visible = false;
break;
}
/*
* The inserter definitely committed. But is it
* old enough that everyone sees it as committed?
*/
xmin = HeapTupleHeaderGetXmin(tuple.t_data);
if (!TransactionIdPrecedes(xmin, OldestXmin))
{
all_visible = false;
break;
}
}
break; break;
case HEAPTUPLE_RECENTLY_DEAD: case HEAPTUPLE_RECENTLY_DEAD:
...@@ -457,12 +544,15 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -457,12 +544,15 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
* from relation. * from relation.
*/ */
nkeep += 1; nkeep += 1;
all_visible = false;
break; break;
case HEAPTUPLE_INSERT_IN_PROGRESS: case HEAPTUPLE_INSERT_IN_PROGRESS:
/* This is an expected case during concurrent vacuum */ /* This is an expected case during concurrent vacuum */
all_visible = false;
break; break;
case HEAPTUPLE_DELETE_IN_PROGRESS: case HEAPTUPLE_DELETE_IN_PROGRESS:
/* This is an expected case during concurrent vacuum */ /* This is an expected case during concurrent vacuum */
all_visible = false;
break; break;
default: default:
elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result"); elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
...@@ -525,12 +615,44 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -525,12 +615,44 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
freespace = PageGetHeapFreeSpace(page); freespace = PageGetHeapFreeSpace(page);
/* Update the all-visible flag on the page */
if (!PageIsAllVisible(page) && all_visible)
{
SetBufferCommitInfoNeedsSave(buf);
PageSetAllVisible(page);
}
else if (PageIsAllVisible(page) && !all_visible)
{
elog(WARNING, "PD_ALL_VISIBLE flag was incorrectly set");
SetBufferCommitInfoNeedsSave(buf);
PageClearAllVisible(page);
/*
* Normally, we would drop the lock on the heap page before
* updating the visibility map, but since this is a can't-happen
* case anyway, don't bother.
*/
visibilitymap_clear(onerel, blkno);
}
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
/* Update the visibility map */
if (!all_visible_according_to_vm && all_visible)
{
visibilitymap_pin(onerel, blkno, &vmbuffer);
LockBuffer(buf, BUFFER_LOCK_SHARE);
if (PageIsAllVisible(page))
visibilitymap_set(onerel, blkno, PageGetLSN(page), &vmbuffer);
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
}
ReleaseBuffer(buf);
/* Remember the location of the last page with nonremovable tuples */ /* Remember the location of the last page with nonremovable tuples */
if (hastup) if (hastup)
vacrelstats->nonempty_pages = blkno + 1; vacrelstats->nonempty_pages = blkno + 1;
UnlockReleaseBuffer(buf);
/* /*
* If we remembered any tuples for deletion, then the page will be * If we remembered any tuples for deletion, then the page will be
* visited again by lazy_vacuum_heap, which will compute and record * visited again by lazy_vacuum_heap, which will compute and record
...@@ -560,6 +682,13 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -560,6 +682,13 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
vacrelstats->num_index_scans++; vacrelstats->num_index_scans++;
} }
/* Release the pin on the visibility map page */
if (BufferIsValid(vmbuffer))
{
ReleaseBuffer(vmbuffer);
vmbuffer = InvalidBuffer;
}
/* Do post-vacuum cleanup and statistics update for each index */ /* Do post-vacuum cleanup and statistics update for each index */
for (i = 0; i < nindexes; i++) for (i = 0; i < nindexes; i++)
lazy_cleanup_index(Irel[i], indstats[i], vacrelstats); lazy_cleanup_index(Irel[i], indstats[i], vacrelstats);
...@@ -572,9 +701,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -572,9 +701,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
tups_vacuumed, vacuumed_pages))); tups_vacuumed, vacuumed_pages)));
ereport(elevel, ereport(elevel,
(errmsg("\"%s\": found %.0f removable, %.0f nonremovable row versions in %u pages", (errmsg("\"%s\": found %.0f removable, %.0f nonremovable row versions in %u out of %u pages",
RelationGetRelationName(onerel), RelationGetRelationName(onerel),
tups_vacuumed, num_tuples, nblocks), tups_vacuumed, num_tuples, scanned_pages, nblocks),
errdetail("%.0f dead row versions cannot be removed yet.\n" errdetail("%.0f dead row versions cannot be removed yet.\n"
"There were %.0f unused item pointers.\n" "There were %.0f unused item pointers.\n"
"%u pages are entirely empty.\n" "%u pages are entirely empty.\n"
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.277 2008/11/26 17:08:57 heikki Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.278 2008/12/03 13:05:22 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -305,6 +305,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp) ...@@ -305,6 +305,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
MemSet(relation, 0, sizeof(RelationData)); MemSet(relation, 0, sizeof(RelationData));
relation->rd_targblock = InvalidBlockNumber; relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks = InvalidBlockNumber; relation->rd_fsm_nblocks = InvalidBlockNumber;
relation->rd_vm_nblocks = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */ /* make sure relation is marked as having no open file yet */
relation->rd_smgr = NULL; relation->rd_smgr = NULL;
...@@ -1377,6 +1378,7 @@ formrdesc(const char *relationName, Oid relationReltype, ...@@ -1377,6 +1378,7 @@ formrdesc(const char *relationName, Oid relationReltype,
relation = (Relation) palloc0(sizeof(RelationData)); relation = (Relation) palloc0(sizeof(RelationData));
relation->rd_targblock = InvalidBlockNumber; relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks = InvalidBlockNumber; relation->rd_fsm_nblocks = InvalidBlockNumber;
relation->rd_vm_nblocks = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */ /* make sure relation is marked as having no open file yet */
relation->rd_smgr = NULL; relation->rd_smgr = NULL;
...@@ -1665,9 +1667,13 @@ RelationReloadIndexInfo(Relation relation) ...@@ -1665,9 +1667,13 @@ RelationReloadIndexInfo(Relation relation)
heap_freetuple(pg_class_tuple); heap_freetuple(pg_class_tuple);
/* We must recalculate physical address in case it changed */ /* We must recalculate physical address in case it changed */
RelationInitPhysicalAddr(relation); RelationInitPhysicalAddr(relation);
/* Must reset targblock and fsm_nblocks in case rel was truncated */ /*
* Must reset targblock, fsm_nblocks and vm_nblocks in case rel was
* truncated
*/
relation->rd_targblock = InvalidBlockNumber; relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks = InvalidBlockNumber; relation->rd_fsm_nblocks = InvalidBlockNumber;
relation->rd_vm_nblocks = InvalidBlockNumber;
/* Must free any AM cached data, too */ /* Must free any AM cached data, too */
if (relation->rd_amcache) if (relation->rd_amcache)
pfree(relation->rd_amcache); pfree(relation->rd_amcache);
...@@ -1751,6 +1757,7 @@ RelationClearRelation(Relation relation, bool rebuild) ...@@ -1751,6 +1757,7 @@ RelationClearRelation(Relation relation, bool rebuild)
{ {
relation->rd_targblock = InvalidBlockNumber; relation->rd_targblock = InvalidBlockNumber;
relation->rd_fsm_nblocks = InvalidBlockNumber; relation->rd_fsm_nblocks = InvalidBlockNumber;
relation->rd_vm_nblocks = InvalidBlockNumber;
if (relation->rd_rel->relkind == RELKIND_INDEX) if (relation->rd_rel->relkind == RELKIND_INDEX)
{ {
relation->rd_isvalid = false; /* needs to be revalidated */ relation->rd_isvalid = false; /* needs to be revalidated */
...@@ -2346,6 +2353,7 @@ RelationBuildLocalRelation(const char *relname, ...@@ -2346,6 +2353,7 @@ RelationBuildLocalRelation(const char *relname,
rel->rd_targblock = InvalidBlockNumber; rel->rd_targblock = InvalidBlockNumber;
rel->rd_fsm_nblocks = InvalidBlockNumber; rel->rd_fsm_nblocks = InvalidBlockNumber;
rel->rd_vm_nblocks = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */ /* make sure relation is marked as having no open file yet */
rel->rd_smgr = NULL; rel->rd_smgr = NULL;
...@@ -3603,6 +3611,7 @@ load_relcache_init_file(void) ...@@ -3603,6 +3611,7 @@ load_relcache_init_file(void)
rel->rd_smgr = NULL; rel->rd_smgr = NULL;
rel->rd_targblock = InvalidBlockNumber; rel->rd_targblock = InvalidBlockNumber;
rel->rd_fsm_nblocks = InvalidBlockNumber; rel->rd_fsm_nblocks = InvalidBlockNumber;
rel->rd_vm_nblocks = InvalidBlockNumber;
if (rel->rd_isnailed) if (rel->rd_isnailed)
rel->rd_refcnt = 1; rel->rd_refcnt = 1;
else else
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/access/htup.h,v 1.104 2008/11/14 01:57:42 alvherre Exp $ * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.105 2008/12/03 13:05:22 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -601,9 +601,10 @@ typedef struct xl_heaptid ...@@ -601,9 +601,10 @@ typedef struct xl_heaptid
typedef struct xl_heap_delete typedef struct xl_heap_delete
{ {
xl_heaptid target; /* deleted tuple id */ xl_heaptid target; /* deleted tuple id */
bool all_visible_cleared; /* PD_ALL_VISIBLE was cleared */
} xl_heap_delete; } xl_heap_delete;
#define SizeOfHeapDelete (offsetof(xl_heap_delete, target) + SizeOfHeapTid) #define SizeOfHeapDelete (offsetof(xl_heap_delete, all_visible_cleared) + sizeof(bool))
/* /*
* We don't store the whole fixed part (HeapTupleHeaderData) of an inserted * We don't store the whole fixed part (HeapTupleHeaderData) of an inserted
...@@ -626,21 +627,24 @@ typedef struct xl_heap_header ...@@ -626,21 +627,24 @@ typedef struct xl_heap_header
typedef struct xl_heap_insert typedef struct xl_heap_insert
{ {
xl_heaptid target; /* inserted tuple id */ xl_heaptid target; /* inserted tuple id */
bool all_visible_cleared; /* PD_ALL_VISIBLE was cleared */
/* xl_heap_header & TUPLE DATA FOLLOWS AT END OF STRUCT */ /* xl_heap_header & TUPLE DATA FOLLOWS AT END OF STRUCT */
} xl_heap_insert; } xl_heap_insert;
#define SizeOfHeapInsert (offsetof(xl_heap_insert, target) + SizeOfHeapTid) #define SizeOfHeapInsert (offsetof(xl_heap_insert, all_visible_cleared) + sizeof(bool))
/* This is what we need to know about update|move|hot_update */ /* This is what we need to know about update|move|hot_update */
typedef struct xl_heap_update typedef struct xl_heap_update
{ {
xl_heaptid target; /* deleted tuple id */ xl_heaptid target; /* deleted tuple id */
ItemPointerData newtid; /* new inserted tuple id */ ItemPointerData newtid; /* new inserted tuple id */
bool all_visible_cleared; /* PD_ALL_VISIBLE was cleared */
bool new_all_visible_cleared; /* same for the page of newtid */
/* NEW TUPLE xl_heap_header (PLUS xmax & xmin IF MOVE OP) */ /* NEW TUPLE xl_heap_header (PLUS xmax & xmin IF MOVE OP) */
/* and TUPLE DATA FOLLOWS AT END OF STRUCT */ /* and TUPLE DATA FOLLOWS AT END OF STRUCT */
} xl_heap_update; } xl_heap_update;
#define SizeOfHeapUpdate (offsetof(xl_heap_update, newtid) + SizeOfIptrData) #define SizeOfHeapUpdate (offsetof(xl_heap_update, new_all_visible_cleared) + sizeof(bool))
/* /*
* This is what we need to know about vacuum page cleanup/redirect * This is what we need to know about vacuum page cleanup/redirect
......
/*-------------------------------------------------------------------------
*
* visibilitymap.h
* visibility map interface
*
*
* Portions Copyright (c) 2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/access/visibilitymap.h,v 1.1 2008/12/03 13:05:22 heikki Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef VISIBILITYMAP_H
#define VISIBILITYMAP_H
#include "utils/rel.h"
#include "storage/buf.h"
#include "storage/itemptr.h"
#include "access/xlogdefs.h"
extern void visibilitymap_clear(Relation rel, BlockNumber heapBlk);
extern void visibilitymap_pin(Relation rel, BlockNumber heapBlk,
Buffer *vmbuf);
extern void visibilitymap_set(Relation rel, BlockNumber heapBlk,
XLogRecPtr recptr, Buffer *vmbuf);
extern bool visibilitymap_test(Relation rel, BlockNumber heapBlk, Buffer *vmbuf);
extern void visibilitymap_truncate(Relation rel, BlockNumber heapblk);
#endif /* VISIBILITYMAP_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/bufpage.h,v 1.84 2008/11/03 20:47:49 tgl Exp $ * $PostgreSQL: pgsql/src/include/storage/bufpage.h,v 1.85 2008/12/03 13:05:22 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -152,8 +152,10 @@ typedef PageHeaderData *PageHeader; ...@@ -152,8 +152,10 @@ typedef PageHeaderData *PageHeader;
#define PD_HAS_FREE_LINES 0x0001 /* are there any unused line pointers? */ #define PD_HAS_FREE_LINES 0x0001 /* are there any unused line pointers? */
#define PD_PAGE_FULL 0x0002 /* not enough free space for new #define PD_PAGE_FULL 0x0002 /* not enough free space for new
* tuple? */ * tuple? */
#define PD_ALL_VISIBLE 0x0004 /* all tuples on page are visible to
* everyone */
#define PD_VALID_FLAG_BITS 0x0003 /* OR of all valid pd_flags bits */ #define PD_VALID_FLAG_BITS 0x0007 /* OR of all valid pd_flags bits */
/* /*
* Page layout version number 0 is for pre-7.3 Postgres releases. * Page layout version number 0 is for pre-7.3 Postgres releases.
...@@ -336,6 +338,13 @@ typedef PageHeaderData *PageHeader; ...@@ -336,6 +338,13 @@ typedef PageHeaderData *PageHeader;
#define PageClearFull(page) \ #define PageClearFull(page) \
(((PageHeader) (page))->pd_flags &= ~PD_PAGE_FULL) (((PageHeader) (page))->pd_flags &= ~PD_PAGE_FULL)
#define PageIsAllVisible(page) \
(((PageHeader) (page))->pd_flags & PD_ALL_VISIBLE)
#define PageSetAllVisible(page) \
(((PageHeader) (page))->pd_flags |= PD_ALL_VISIBLE)
#define PageClearAllVisible(page) \
(((PageHeader) (page))->pd_flags &= ~PD_ALL_VISIBLE)
#define PageIsPrunable(page, oldestxmin) \ #define PageIsPrunable(page, oldestxmin) \
( \ ( \
AssertMacro(TransactionIdIsNormal(oldestxmin)), \ AssertMacro(TransactionIdIsNormal(oldestxmin)), \
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/relfilenode.h,v 1.20 2008/11/19 10:34:52 heikki Exp $ * $PostgreSQL: pgsql/src/include/storage/relfilenode.h,v 1.21 2008/12/03 13:05:22 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -24,14 +24,15 @@ typedef enum ForkNumber ...@@ -24,14 +24,15 @@ typedef enum ForkNumber
{ {
InvalidForkNumber = -1, InvalidForkNumber = -1,
MAIN_FORKNUM = 0, MAIN_FORKNUM = 0,
FSM_FORKNUM FSM_FORKNUM,
VISIBILITYMAP_FORKNUM
/* /*
* NOTE: if you add a new fork, change MAX_FORKNUM below and update the * NOTE: if you add a new fork, change MAX_FORKNUM below and update the
* forkNames array in catalog.c * forkNames array in catalog.c
*/ */
} ForkNumber; } ForkNumber;
#define MAX_FORKNUM FSM_FORKNUM #define MAX_FORKNUM VISIBILITYMAP_FORKNUM
/* /*
* RelFileNode must provide all that we need to know to physically access * RelFileNode must provide all that we need to know to physically access
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.109 2008/11/26 17:08:58 heikki Exp $ * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.110 2008/12/03 13:05:22 heikki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -195,8 +195,12 @@ typedef struct RelationData ...@@ -195,8 +195,12 @@ typedef struct RelationData
List *rd_indpred; /* index predicate tree, if any */ List *rd_indpred; /* index predicate tree, if any */
void *rd_amcache; /* available for use by index AM */ void *rd_amcache; /* available for use by index AM */
/* size of the FSM, or InvalidBlockNumber if not known yet */ /*
* sizes of the free space and visibility map forks, or InvalidBlockNumber
* if not known yet
*/
BlockNumber rd_fsm_nblocks; BlockNumber rd_fsm_nblocks;
BlockNumber rd_vm_nblocks;
/* use "struct" here to avoid needing to include pgstat.h: */ /* use "struct" here to avoid needing to include pgstat.h: */
struct PgStat_TableStatus *pgstat_info; /* statistics collection area */ struct PgStat_TableStatus *pgstat_info; /* statistics collection area */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment