mirror of
https://github.com/postgres/postgres.git
synced 2025-04-27 22:56:53 +03:00
Performance improvement for MultiRecordFreeSpace on large relations ---
avoid O(N^2) behavior. Problem noted and fixed by Stephen Marshall <smarshall@wsicorp.com>, with some help from Tom Lane.
This commit is contained in:
parent
de96cd5e3a
commit
b2735fcd52
@ -13,7 +13,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.237 2002/09/04 20:31:16 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.238 2002/09/20 19:56:01 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1321,9 +1321,10 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
|
|||||||
pfree(vtlinks);
|
pfree(vtlinks);
|
||||||
}
|
}
|
||||||
|
|
||||||
elog(elevel, "Pages %u: Changed %u, reaped %u, Empty %u, New %u; \
|
elog(elevel, "Pages %u: Changed %u, reaped %u, Empty %u, New %u; "
|
||||||
Tup %.0f: Vac %.0f, Keep/VTL %.0f/%u, UnUsed %.0f, MinLen %lu, MaxLen %lu; \
|
"Tup %.0f: Vac %.0f, Keep/VTL %.0f/%u, UnUsed %.0f, MinLen %lu, "
|
||||||
Re-using: Free/Avail. Space %.0f/%.0f; EndEmpty/Avail. Pages %u/%u.\n\t%s",
|
"MaxLen %lu; Re-using: Free/Avail. Space %.0f/%.0f; "
|
||||||
|
"EndEmpty/Avail. Pages %u/%u.\n\t%s",
|
||||||
nblocks, changed_pages, vacuum_pages->num_pages, empty_pages,
|
nblocks, changed_pages, vacuum_pages->num_pages, empty_pages,
|
||||||
new_pages, num_tuples, tups_vacuumed,
|
new_pages, num_tuples, tups_vacuumed,
|
||||||
nkeep, vacrelstats->num_vtlinks,
|
nkeep, vacrelstats->num_vtlinks,
|
||||||
@ -2597,8 +2598,8 @@ scan_index(Relation indrel, double num_tuples)
|
|||||||
{
|
{
|
||||||
if (stats->num_index_tuples > num_tuples ||
|
if (stats->num_index_tuples > num_tuples ||
|
||||||
!vac_is_partial_index(indrel))
|
!vac_is_partial_index(indrel))
|
||||||
elog(WARNING, "Index %s: NUMBER OF INDEX' TUPLES (%.0f) IS NOT THE SAME AS HEAP' (%.0f).\
|
elog(WARNING, "Index %s: NUMBER OF INDEX' TUPLES (%.0f) IS NOT THE SAME AS HEAP' (%.0f)."
|
||||||
\n\tRecreate the index.",
|
"\n\tRecreate the index.",
|
||||||
RelationGetRelationName(indrel),
|
RelationGetRelationName(indrel),
|
||||||
stats->num_index_tuples, num_tuples);
|
stats->num_index_tuples, num_tuples);
|
||||||
}
|
}
|
||||||
@ -2651,8 +2652,8 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
|
|||||||
{
|
{
|
||||||
if (stats->num_index_tuples > num_tuples + keep_tuples ||
|
if (stats->num_index_tuples > num_tuples + keep_tuples ||
|
||||||
!vac_is_partial_index(indrel))
|
!vac_is_partial_index(indrel))
|
||||||
elog(WARNING, "Index %s: NUMBER OF INDEX' TUPLES (%.0f) IS NOT THE SAME AS HEAP' (%.0f).\
|
elog(WARNING, "Index %s: NUMBER OF INDEX' TUPLES (%.0f) IS NOT THE SAME AS HEAP' (%.0f)."
|
||||||
\n\tRecreate the index.",
|
"\n\tRecreate the index.",
|
||||||
RelationGetRelationName(indrel),
|
RelationGetRelationName(indrel),
|
||||||
stats->num_index_tuples, num_tuples);
|
stats->num_index_tuples, num_tuples);
|
||||||
}
|
}
|
||||||
@ -2731,35 +2732,32 @@ vac_update_fsm(Relation onerel, VacPageList fraged_pages,
|
|||||||
{
|
{
|
||||||
int nPages = fraged_pages->num_pages;
|
int nPages = fraged_pages->num_pages;
|
||||||
int i;
|
int i;
|
||||||
BlockNumber *pages;
|
PageFreeSpaceInfo *pageSpaces;
|
||||||
Size *spaceAvail;
|
|
||||||
|
|
||||||
/* +1 to avoid palloc(0) */
|
/* +1 to avoid palloc(0) */
|
||||||
pages = (BlockNumber *) palloc((nPages + 1) * sizeof(BlockNumber));
|
pageSpaces = (PageFreeSpaceInfo *)
|
||||||
spaceAvail = (Size *) palloc((nPages + 1) * sizeof(Size));
|
palloc((nPages + 1) * sizeof(PageFreeSpaceInfo));
|
||||||
|
|
||||||
for (i = 0; i < nPages; i++)
|
for (i = 0; i < nPages; i++)
|
||||||
{
|
{
|
||||||
pages[i] = fraged_pages->pagedesc[i]->blkno;
|
pageSpaces[i].blkno = fraged_pages->pagedesc[i]->blkno;
|
||||||
spaceAvail[i] = fraged_pages->pagedesc[i]->free;
|
pageSpaces[i].avail = fraged_pages->pagedesc[i]->free;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fraged_pages may contain entries for pages that we later
|
* fraged_pages may contain entries for pages that we later
|
||||||
* decided to truncate from the relation; don't enter them into
|
* decided to truncate from the relation; don't enter them into
|
||||||
* the map!
|
* the free space map!
|
||||||
*/
|
*/
|
||||||
if (pages[i] >= rel_pages)
|
if (pageSpaces[i].blkno >= rel_pages)
|
||||||
{
|
{
|
||||||
nPages = i;
|
nPages = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiRecordFreeSpace(&onerel->rd_node,
|
MultiRecordFreeSpace(&onerel->rd_node, 0, nPages, pageSpaces);
|
||||||
0, MaxBlockNumber,
|
|
||||||
nPages, pages, spaceAvail);
|
pfree(pageSpaces);
|
||||||
pfree(pages);
|
|
||||||
pfree(spaceAvail);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy a VacPage structure */
|
/* Copy a VacPage structure */
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/commands/vacuumlazy.c,v 1.19 2002/09/04 20:31:17 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/commands/vacuumlazy.c,v 1.20 2002/09/20 19:56:01 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -87,9 +87,8 @@ typedef struct LVRelStats
|
|||||||
/* We use a simple array until it fills up, then convert to heap */
|
/* We use a simple array until it fills up, then convert to heap */
|
||||||
bool fs_is_heap; /* are we using heap organization? */
|
bool fs_is_heap; /* are we using heap organization? */
|
||||||
int num_free_pages; /* current # of entries */
|
int num_free_pages; /* current # of entries */
|
||||||
int max_free_pages; /* # slots allocated in arrays */
|
int max_free_pages; /* # slots allocated in array */
|
||||||
BlockNumber *free_pages; /* array or heap of block numbers */
|
PageFreeSpaceInfo *free_pages; /* array or heap of blkno/avail */
|
||||||
Size *free_spaceavail; /* array or heap of available space */
|
|
||||||
} LVRelStats;
|
} LVRelStats;
|
||||||
|
|
||||||
|
|
||||||
@ -119,6 +118,7 @@ static bool lazy_tid_reaped(ItemPointer itemptr, void *state);
|
|||||||
static bool dummy_tid_reaped(ItemPointer itemptr, void *state);
|
static bool dummy_tid_reaped(ItemPointer itemptr, void *state);
|
||||||
static void lazy_update_fsm(Relation onerel, LVRelStats *vacrelstats);
|
static void lazy_update_fsm(Relation onerel, LVRelStats *vacrelstats);
|
||||||
static int vac_cmp_itemptr(const void *left, const void *right);
|
static int vac_cmp_itemptr(const void *left, const void *right);
|
||||||
|
static int vac_cmp_page_spaces(const void *left, const void *right);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -432,8 +432,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
|||||||
lazy_scan_index(Irel[i], vacrelstats);
|
lazy_scan_index(Irel[i], vacrelstats);
|
||||||
}
|
}
|
||||||
|
|
||||||
elog(elevel, "Pages %u: Changed %u, Empty %u; \
|
elog(elevel, "Pages %u: Changed %u, Empty %u; Tup %.0f: Vac %.0f, Keep %.0f, UnUsed %.0f.\n\tTotal %s",
|
||||||
Tup %.0f: Vac %.0f, Keep %.0f, UnUsed %.0f.\n\tTotal %s",
|
|
||||||
nblocks, changed_pages, empty_pages,
|
nblocks, changed_pages, empty_pages,
|
||||||
num_tuples, tups_vacuumed, nkeep, nunused,
|
num_tuples, tups_vacuumed, nkeep, nunused,
|
||||||
vac_show_rusage(&ru0));
|
vac_show_rusage(&ru0));
|
||||||
@ -662,8 +661,7 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
|
|||||||
{
|
{
|
||||||
BlockNumber old_rel_pages = vacrelstats->rel_pages;
|
BlockNumber old_rel_pages = vacrelstats->rel_pages;
|
||||||
BlockNumber new_rel_pages;
|
BlockNumber new_rel_pages;
|
||||||
BlockNumber *pages;
|
PageFreeSpaceInfo *pageSpaces;
|
||||||
Size *spaceavail;
|
|
||||||
int n;
|
int n;
|
||||||
int i,
|
int i,
|
||||||
j;
|
j;
|
||||||
@ -736,20 +734,20 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
|
|||||||
* Drop free-space info for removed blocks; these must not get entered
|
* Drop free-space info for removed blocks; these must not get entered
|
||||||
* into the FSM!
|
* into the FSM!
|
||||||
*/
|
*/
|
||||||
pages = vacrelstats->free_pages;
|
pageSpaces = vacrelstats->free_pages;
|
||||||
spaceavail = vacrelstats->free_spaceavail;
|
|
||||||
n = vacrelstats->num_free_pages;
|
n = vacrelstats->num_free_pages;
|
||||||
j = 0;
|
j = 0;
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
if (pages[i] < new_rel_pages)
|
if (pageSpaces[i].blkno < new_rel_pages)
|
||||||
{
|
{
|
||||||
pages[j] = pages[i];
|
pageSpaces[j] = pageSpaces[i];
|
||||||
spaceavail[j] = spaceavail[i];
|
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vacrelstats->num_free_pages = j;
|
vacrelstats->num_free_pages = j;
|
||||||
|
/* We destroyed the heap ordering, so mark array unordered */
|
||||||
|
vacrelstats->fs_is_heap = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We keep the exclusive lock until commit (perhaps not necessary)?
|
* We keep the exclusive lock until commit (perhaps not necessary)?
|
||||||
@ -913,10 +911,8 @@ lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks)
|
|||||||
vacrelstats->fs_is_heap = false;
|
vacrelstats->fs_is_heap = false;
|
||||||
vacrelstats->num_free_pages = 0;
|
vacrelstats->num_free_pages = 0;
|
||||||
vacrelstats->max_free_pages = maxpages;
|
vacrelstats->max_free_pages = maxpages;
|
||||||
vacrelstats->free_pages = (BlockNumber *)
|
vacrelstats->free_pages = (PageFreeSpaceInfo *)
|
||||||
palloc(maxpages * sizeof(BlockNumber));
|
palloc(maxpages * sizeof(PageFreeSpaceInfo));
|
||||||
vacrelstats->free_spaceavail = (Size *)
|
|
||||||
palloc(maxpages * sizeof(Size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -946,8 +942,7 @@ lazy_record_free_space(LVRelStats *vacrelstats,
|
|||||||
BlockNumber page,
|
BlockNumber page,
|
||||||
Size avail)
|
Size avail)
|
||||||
{
|
{
|
||||||
BlockNumber *pages;
|
PageFreeSpaceInfo *pageSpaces;
|
||||||
Size *spaceavail;
|
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
/* Ignore pages with little free space */
|
/* Ignore pages with little free space */
|
||||||
@ -955,15 +950,14 @@ lazy_record_free_space(LVRelStats *vacrelstats,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* Copy pointers to local variables for notational simplicity */
|
/* Copy pointers to local variables for notational simplicity */
|
||||||
pages = vacrelstats->free_pages;
|
pageSpaces = vacrelstats->free_pages;
|
||||||
spaceavail = vacrelstats->free_spaceavail;
|
|
||||||
n = vacrelstats->max_free_pages;
|
n = vacrelstats->max_free_pages;
|
||||||
|
|
||||||
/* If we haven't filled the array yet, just keep adding entries */
|
/* If we haven't filled the array yet, just keep adding entries */
|
||||||
if (vacrelstats->num_free_pages < n)
|
if (vacrelstats->num_free_pages < n)
|
||||||
{
|
{
|
||||||
pages[vacrelstats->num_free_pages] = page;
|
pageSpaces[vacrelstats->num_free_pages].blkno = page;
|
||||||
spaceavail[vacrelstats->num_free_pages] = avail;
|
pageSpaces[vacrelstats->num_free_pages].avail = avail;
|
||||||
vacrelstats->num_free_pages++;
|
vacrelstats->num_free_pages++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -971,7 +965,7 @@ lazy_record_free_space(LVRelStats *vacrelstats,
|
|||||||
/*----------
|
/*----------
|
||||||
* The rest of this routine works with "heap" organization of the
|
* The rest of this routine works with "heap" organization of the
|
||||||
* free space arrays, wherein we maintain the heap property
|
* free space arrays, wherein we maintain the heap property
|
||||||
* spaceavail[(j-1) div 2] <= spaceavail[j] for 0 < j < n.
|
* avail[(j-1) div 2] <= avail[j] for 0 < j < n.
|
||||||
* In particular, the zero'th element always has the smallest available
|
* In particular, the zero'th element always has the smallest available
|
||||||
* space and can be discarded to make room for a new page with more space.
|
* space and can be discarded to make room for a new page with more space.
|
||||||
* See Knuth's discussion of heap-based priority queues, sec 5.2.3;
|
* See Knuth's discussion of heap-based priority queues, sec 5.2.3;
|
||||||
@ -991,8 +985,8 @@ lazy_record_free_space(LVRelStats *vacrelstats,
|
|||||||
|
|
||||||
while (--l >= 0)
|
while (--l >= 0)
|
||||||
{
|
{
|
||||||
BlockNumber R = pages[l];
|
BlockNumber R = pageSpaces[l].blkno;
|
||||||
Size K = spaceavail[l];
|
Size K = pageSpaces[l].avail;
|
||||||
int i; /* i is where the "hole" is */
|
int i; /* i is where the "hole" is */
|
||||||
|
|
||||||
i = l;
|
i = l;
|
||||||
@ -1002,23 +996,22 @@ lazy_record_free_space(LVRelStats *vacrelstats,
|
|||||||
|
|
||||||
if (j >= n)
|
if (j >= n)
|
||||||
break;
|
break;
|
||||||
if (j + 1 < n && spaceavail[j] > spaceavail[j + 1])
|
if (j + 1 < n && pageSpaces[j].avail > pageSpaces[j + 1].avail)
|
||||||
j++;
|
j++;
|
||||||
if (K <= spaceavail[j])
|
if (K <= pageSpaces[j].avail)
|
||||||
break;
|
break;
|
||||||
pages[i] = pages[j];
|
pageSpaces[i] = pageSpaces[j];
|
||||||
spaceavail[i] = spaceavail[j];
|
|
||||||
i = j;
|
i = j;
|
||||||
}
|
}
|
||||||
pages[i] = R;
|
pageSpaces[i].blkno = R;
|
||||||
spaceavail[i] = K;
|
pageSpaces[i].avail = K;
|
||||||
}
|
}
|
||||||
|
|
||||||
vacrelstats->fs_is_heap = true;
|
vacrelstats->fs_is_heap = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If new page has more than zero'th entry, insert it into heap */
|
/* If new page has more than zero'th entry, insert it into heap */
|
||||||
if (avail > spaceavail[0])
|
if (avail > pageSpaces[0].avail)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Notionally, we replace the zero'th entry with the new data, and
|
* Notionally, we replace the zero'th entry with the new data, and
|
||||||
@ -1034,16 +1027,15 @@ lazy_record_free_space(LVRelStats *vacrelstats,
|
|||||||
|
|
||||||
if (j >= n)
|
if (j >= n)
|
||||||
break;
|
break;
|
||||||
if (j + 1 < n && spaceavail[j] > spaceavail[j + 1])
|
if (j + 1 < n && pageSpaces[j].avail > pageSpaces[j + 1].avail)
|
||||||
j++;
|
j++;
|
||||||
if (avail <= spaceavail[j])
|
if (avail <= pageSpaces[j].avail)
|
||||||
break;
|
break;
|
||||||
pages[i] = pages[j];
|
pageSpaces[i] = pageSpaces[j];
|
||||||
spaceavail[i] = spaceavail[j];
|
|
||||||
i = j;
|
i = j;
|
||||||
}
|
}
|
||||||
pages[i] = page;
|
pageSpaces[i].blkno = page;
|
||||||
spaceavail[i] = avail;
|
pageSpaces[i].avail = avail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1085,16 +1077,17 @@ dummy_tid_reaped(ItemPointer itemptr, void *state)
|
|||||||
static void
|
static void
|
||||||
lazy_update_fsm(Relation onerel, LVRelStats *vacrelstats)
|
lazy_update_fsm(Relation onerel, LVRelStats *vacrelstats)
|
||||||
{
|
{
|
||||||
|
PageFreeSpaceInfo *pageSpaces = vacrelstats->free_pages;
|
||||||
|
int nPages = vacrelstats->num_free_pages;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since MultiRecordFreeSpace doesn't currently impose any
|
* Sort data into order, as required by MultiRecordFreeSpace.
|
||||||
* restrictions on the ordering of the input, we can just pass it the
|
|
||||||
* arrays as-is, whether they are in heap or linear order.
|
|
||||||
*/
|
*/
|
||||||
MultiRecordFreeSpace(&onerel->rd_node,
|
if (nPages > 1)
|
||||||
0, MaxBlockNumber,
|
qsort(pageSpaces, nPages, sizeof(PageFreeSpaceInfo),
|
||||||
vacrelstats->num_free_pages,
|
vac_cmp_page_spaces);
|
||||||
vacrelstats->free_pages,
|
|
||||||
vacrelstats->free_spaceavail);
|
MultiRecordFreeSpace(&onerel->rd_node, 0, nPages, pageSpaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1126,3 +1119,16 @@ vac_cmp_itemptr(const void *left, const void *right)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vac_cmp_page_spaces(const void *left, const void *right)
|
||||||
|
{
|
||||||
|
PageFreeSpaceInfo *linfo = (PageFreeSpaceInfo *) left;
|
||||||
|
PageFreeSpaceInfo *rinfo = (PageFreeSpaceInfo *) right;
|
||||||
|
|
||||||
|
if (linfo->blkno < rinfo->blkno)
|
||||||
|
return -1;
|
||||||
|
else if (linfo->blkno > rinfo->blkno)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/storage/freespace/freespace.c,v 1.13 2002/09/04 20:31:25 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/storage/freespace/freespace.c,v 1.14 2002/09/20 19:56:01 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTES:
|
* NOTES:
|
||||||
@ -99,6 +99,7 @@ struct FSMRelation
|
|||||||
* about */
|
* about */
|
||||||
int numChunks; /* number of FSMChunks allocated to rel */
|
int numChunks; /* number of FSMChunks allocated to rel */
|
||||||
FSMChunk *relChunks; /* linked list of page info chunks */
|
FSMChunk *relChunks; /* linked list of page info chunks */
|
||||||
|
FSMChunk *lastChunk; /* last chunk in linked list */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -142,6 +143,7 @@ static bool lookup_fsm_page_entry(FSMRelation *fsmrel, BlockNumber page,
|
|||||||
static bool insert_fsm_page_entry(FSMRelation *fsmrel,
|
static bool insert_fsm_page_entry(FSMRelation *fsmrel,
|
||||||
BlockNumber page, Size spaceAvail,
|
BlockNumber page, Size spaceAvail,
|
||||||
FSMChunk *chunk, int chunkRelIndex);
|
FSMChunk *chunk, int chunkRelIndex);
|
||||||
|
static bool append_fsm_chunk(FSMRelation *fsmrel);
|
||||||
static bool push_fsm_page_entry(BlockNumber page, Size spaceAvail,
|
static bool push_fsm_page_entry(BlockNumber page, Size spaceAvail,
|
||||||
FSMChunk *chunk, int chunkRelIndex);
|
FSMChunk *chunk, int chunkRelIndex);
|
||||||
static void delete_fsm_page_entry(FSMRelation *fsmrel, FSMChunk *chunk,
|
static void delete_fsm_page_entry(FSMRelation *fsmrel, FSMChunk *chunk,
|
||||||
@ -359,21 +361,20 @@ RecordAndGetPageWithFreeSpace(RelFileNode *rel,
|
|||||||
* MultiRecordFreeSpace - record available-space info about multiple pages
|
* MultiRecordFreeSpace - record available-space info about multiple pages
|
||||||
* of a relation in one call.
|
* of a relation in one call.
|
||||||
*
|
*
|
||||||
* First, if minPage <= maxPage, the FSM must discard any entries it has for
|
* First, the FSM must discard any entries it has for pages >= minPage.
|
||||||
* pages in that page number range (inclusive). This allows obsolete info
|
* This allows obsolete info to be discarded (for example, it is used when
|
||||||
* to be discarded. Second, if nPages > 0, record the page numbers and free
|
* truncating a relation). Any entries before minPage should be kept.
|
||||||
* space amounts in the given arrays. As with RecordFreeSpace, the FSM is at
|
*
|
||||||
* liberty to discard some of the information. However, it *must* discard
|
* Second, if nPages > 0, record the page numbers and free space amounts in
|
||||||
* previously stored info in the minPage..maxPage range (for example, this
|
* the given pageSpaces[] array. As with RecordFreeSpace, the FSM is at
|
||||||
* case is used to remove info about deleted pages during relation truncation).
|
* liberty to discard some of this information. The pageSpaces[] array must
|
||||||
|
* be sorted in order by blkno, and may not contain entries before minPage.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
MultiRecordFreeSpace(RelFileNode *rel,
|
MultiRecordFreeSpace(RelFileNode *rel,
|
||||||
BlockNumber minPage,
|
BlockNumber minPage,
|
||||||
BlockNumber maxPage,
|
|
||||||
int nPages,
|
int nPages,
|
||||||
BlockNumber *pages,
|
PageFreeSpaceInfo *pageSpaces)
|
||||||
Size *spaceAvail)
|
|
||||||
{
|
{
|
||||||
FSMRelation *fsmrel;
|
FSMRelation *fsmrel;
|
||||||
int i;
|
int i;
|
||||||
@ -383,59 +384,64 @@ MultiRecordFreeSpace(RelFileNode *rel,
|
|||||||
if (fsmrel)
|
if (fsmrel)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Remove entries in specified range
|
* Remove entries >= minPage
|
||||||
*/
|
*/
|
||||||
if (minPage <= maxPage)
|
|
||||||
{
|
|
||||||
FSMChunk *chunk;
|
FSMChunk *chunk;
|
||||||
int chunkRelIndex;
|
int chunkRelIndex;
|
||||||
bool done;
|
|
||||||
|
|
||||||
/* Use lookup to locate first entry >= minPage */
|
/* Use lookup to locate first entry >= minPage */
|
||||||
lookup_fsm_page_entry(fsmrel, minPage, &chunk, &chunkRelIndex);
|
lookup_fsm_page_entry(fsmrel, minPage, &chunk, &chunkRelIndex);
|
||||||
/* Set free space to 0 for each page within range */
|
/* Set free space to 0 for each page >= minPage */
|
||||||
done = false;
|
while (chunk)
|
||||||
while (chunk && !done)
|
|
||||||
{
|
{
|
||||||
int numPages = chunk->numPages;
|
int numPages = chunk->numPages;
|
||||||
|
|
||||||
for (; chunkRelIndex < numPages; chunkRelIndex++)
|
for (i = chunkRelIndex; i < numPages; i++)
|
||||||
{
|
chunk->bytes[i] = 0;
|
||||||
if (chunk->pages[chunkRelIndex] > maxPage)
|
|
||||||
{
|
|
||||||
done = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
chunk->bytes[chunkRelIndex] = 0;
|
|
||||||
}
|
|
||||||
chunk = chunk->next;
|
chunk = chunk->next;
|
||||||
chunkRelIndex = 0;
|
chunkRelIndex = 0;
|
||||||
}
|
}
|
||||||
/* Now compact out the zeroed entries */
|
/* Now compact out the zeroed entries, along with any other junk */
|
||||||
compact_fsm_page_list(fsmrel);
|
compact_fsm_page_list(fsmrel);
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add new entries, if appropriate.
|
* Add new entries, if appropriate.
|
||||||
*
|
*
|
||||||
* XXX we could probably be smarter about this than doing it
|
* This can be much cheaper than a full fsm_record_free_space()
|
||||||
* completely separately for each one. FIXME later.
|
* call because we know we are appending to the end of the relation.
|
||||||
*
|
|
||||||
* One thing we can do is short-circuit the process entirely if a
|
|
||||||
* page (a) has too little free space to be recorded, and (b) is
|
|
||||||
* within the minPage..maxPage range --- then we deleted any old
|
|
||||||
* entry above, and we aren't going to make a new one. This is
|
|
||||||
* particularly useful since in most cases, all the passed pages
|
|
||||||
* will in fact be in the minPage..maxPage range.
|
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < nPages; i++)
|
for (i = 0; i < nPages; i++)
|
||||||
{
|
{
|
||||||
BlockNumber page = pages[i];
|
BlockNumber page = pageSpaces[i].blkno;
|
||||||
Size avail = spaceAvail[i];
|
Size avail = pageSpaces[i].avail;
|
||||||
|
FSMChunk *chunk;
|
||||||
|
|
||||||
if (avail >= fsmrel->threshold ||
|
/* Check caller provides sorted data */
|
||||||
page < minPage || page > maxPage)
|
if (i > 0 ? (page <= pageSpaces[i-1].blkno) : (page < minPage))
|
||||||
fsm_record_free_space(fsmrel, page, avail);
|
elog(ERROR, "MultiRecordFreeSpace: data not in page order");
|
||||||
|
|
||||||
|
/* Ignore pages too small to fit */
|
||||||
|
if (avail < fsmrel->threshold)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Get another chunk if needed */
|
||||||
|
/* We may need to loop if acquire_fsm_free_space() fails */
|
||||||
|
while ((chunk = fsmrel->lastChunk) == NULL ||
|
||||||
|
chunk->numPages >= CHUNKPAGES)
|
||||||
|
{
|
||||||
|
if (!append_fsm_chunk(fsmrel))
|
||||||
|
acquire_fsm_free_space();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recheck in case threshold was raised by acquire */
|
||||||
|
if (avail < fsmrel->threshold)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Okay to store */
|
||||||
|
chunk->pages[chunk->numPages] = page;
|
||||||
|
chunk->bytes[chunk->numPages] = (ItemLength) avail;
|
||||||
|
chunk->numPages++;
|
||||||
|
fsmrel->numPages++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LWLockRelease(FreeSpaceLock);
|
LWLockRelease(FreeSpaceLock);
|
||||||
@ -538,6 +544,7 @@ create_fsm_rel(RelFileNode *rel)
|
|||||||
fsmrel->numPages = 0;
|
fsmrel->numPages = 0;
|
||||||
fsmrel->numChunks = 0;
|
fsmrel->numChunks = 0;
|
||||||
fsmrel->relChunks = NULL;
|
fsmrel->relChunks = NULL;
|
||||||
|
fsmrel->lastChunk = NULL;
|
||||||
/* Discard lowest-priority existing rel, if we are over limit */
|
/* Discard lowest-priority existing rel, if we are over limit */
|
||||||
if (FreeSpaceMap->numRels >= MaxFSMRelations)
|
if (FreeSpaceMap->numRels >= MaxFSMRelations)
|
||||||
delete_fsm_rel(FreeSpaceMap->relListTail);
|
delete_fsm_rel(FreeSpaceMap->relListTail);
|
||||||
@ -847,29 +854,12 @@ insert_fsm_page_entry(FSMRelation *fsmrel, BlockNumber page, Size spaceAvail,
|
|||||||
if (fsmrel->numPages >= fsmrel->numChunks * CHUNKPAGES)
|
if (fsmrel->numPages >= fsmrel->numChunks * CHUNKPAGES)
|
||||||
{
|
{
|
||||||
/* No free space within chunk list, so need another chunk */
|
/* No free space within chunk list, so need another chunk */
|
||||||
FSMChunk *newChunk;
|
if (!append_fsm_chunk(fsmrel))
|
||||||
|
|
||||||
if ((newChunk = FreeSpaceMap->freeChunks) == NULL)
|
|
||||||
return false; /* can't do it */
|
return false; /* can't do it */
|
||||||
FreeSpaceMap->freeChunks = newChunk->next;
|
|
||||||
FreeSpaceMap->numFreeChunks--;
|
|
||||||
newChunk->next = NULL;
|
|
||||||
newChunk->numPages = 0;
|
|
||||||
if (fsmrel->relChunks == NULL)
|
|
||||||
fsmrel->relChunks = newChunk;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FSMChunk *priorChunk = fsmrel->relChunks;
|
|
||||||
|
|
||||||
while (priorChunk->next != NULL)
|
|
||||||
priorChunk = priorChunk->next;
|
|
||||||
priorChunk->next = newChunk;
|
|
||||||
}
|
|
||||||
fsmrel->numChunks++;
|
|
||||||
if (chunk == NULL)
|
if (chunk == NULL)
|
||||||
{
|
{
|
||||||
/* Original search found that new page belongs at end */
|
/* Original search found that new page belongs at end */
|
||||||
chunk = newChunk;
|
chunk = fsmrel->lastChunk;
|
||||||
chunkRelIndex = 0;
|
chunkRelIndex = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -900,6 +890,38 @@ insert_fsm_page_entry(FSMRelation *fsmrel, BlockNumber page, Size spaceAvail,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add one chunk to a FSMRelation's chunk list, if possible.
|
||||||
|
*
|
||||||
|
* Returns TRUE if successful, FALSE if no space available. Note that on
|
||||||
|
* success, the new chunk is easily accessible via fsmrel->lastChunk.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
append_fsm_chunk(FSMRelation *fsmrel)
|
||||||
|
{
|
||||||
|
FSMChunk *newChunk;
|
||||||
|
|
||||||
|
/* Remove a chunk from the freelist */
|
||||||
|
if ((newChunk = FreeSpaceMap->freeChunks) == NULL)
|
||||||
|
return false; /* can't do it */
|
||||||
|
FreeSpaceMap->freeChunks = newChunk->next;
|
||||||
|
FreeSpaceMap->numFreeChunks--;
|
||||||
|
|
||||||
|
/* Initialize chunk to empty */
|
||||||
|
newChunk->next = NULL;
|
||||||
|
newChunk->numPages = 0;
|
||||||
|
|
||||||
|
/* Link it into FSMRelation */
|
||||||
|
if (fsmrel->relChunks == NULL)
|
||||||
|
fsmrel->relChunks = newChunk;
|
||||||
|
else
|
||||||
|
fsmrel->lastChunk->next = newChunk;
|
||||||
|
fsmrel->lastChunk = newChunk;
|
||||||
|
fsmrel->numChunks++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Auxiliary routine for insert_fsm_page_entry: try to push entries to the
|
* Auxiliary routine for insert_fsm_page_entry: try to push entries to the
|
||||||
* right to insert at chunk/chunkRelIndex. Return TRUE if successful.
|
* right to insert at chunk/chunkRelIndex. Return TRUE if successful.
|
||||||
@ -1016,6 +1038,7 @@ compact_fsm_page_list(FSMRelation *fsmrel)
|
|||||||
fsmrel->numPages = 0;
|
fsmrel->numPages = 0;
|
||||||
fsmrel->numChunks = 0;
|
fsmrel->numChunks = 0;
|
||||||
fsmrel->relChunks = NULL;
|
fsmrel->relChunks = NULL;
|
||||||
|
fsmrel->lastChunk = NULL;
|
||||||
free_chunk_chain(dstChunk);
|
free_chunk_chain(dstChunk);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1026,6 +1049,7 @@ compact_fsm_page_list(FSMRelation *fsmrel)
|
|||||||
dstChunk->numPages = dstIndex;
|
dstChunk->numPages = dstIndex;
|
||||||
free_chunk_chain(dstChunk->next);
|
free_chunk_chain(dstChunk->next);
|
||||||
dstChunk->next = NULL;
|
dstChunk->next = NULL;
|
||||||
|
fsmrel->lastChunk = dstChunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/storage/smgr/smgr.c,v 1.60 2002/09/04 20:31:26 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/storage/smgr/smgr.c,v 1.61 2002/09/20 19:56:01 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -410,9 +410,7 @@ smgrtruncate(int16 which, Relation reln, BlockNumber nblocks)
|
|||||||
* for the about-to-be-deleted blocks. We want to be sure it
|
* for the about-to-be-deleted blocks. We want to be sure it
|
||||||
* won't return bogus block numbers later on.
|
* won't return bogus block numbers later on.
|
||||||
*/
|
*/
|
||||||
MultiRecordFreeSpace(&reln->rd_node,
|
MultiRecordFreeSpace(&reln->rd_node, nblocks, 0, NULL);
|
||||||
nblocks, MaxBlockNumber,
|
|
||||||
0, NULL, NULL);
|
|
||||||
|
|
||||||
newblks = (*(smgrsw[which].smgr_truncate)) (reln, nblocks);
|
newblks = (*(smgrsw[which].smgr_truncate)) (reln, nblocks);
|
||||||
if (newblks == InvalidBlockNumber)
|
if (newblks == InvalidBlockNumber)
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: freespace.h,v 1.7 2002/06/20 20:29:52 momjian Exp $
|
* $Id: freespace.h,v 1.8 2002/09/20 19:56:01 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -18,6 +18,16 @@
|
|||||||
#include "storage/relfilenode.h"
|
#include "storage/relfilenode.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* exported types
|
||||||
|
*/
|
||||||
|
typedef struct PageFreeSpaceInfo
|
||||||
|
{
|
||||||
|
BlockNumber blkno; /* which page in relation */
|
||||||
|
Size avail; /* space available on this page */
|
||||||
|
} PageFreeSpaceInfo;
|
||||||
|
|
||||||
|
|
||||||
extern int MaxFSMRelations;
|
extern int MaxFSMRelations;
|
||||||
extern int MaxFSMPages;
|
extern int MaxFSMPages;
|
||||||
|
|
||||||
@ -37,10 +47,8 @@ extern BlockNumber RecordAndGetPageWithFreeSpace(RelFileNode *rel,
|
|||||||
Size spaceNeeded);
|
Size spaceNeeded);
|
||||||
extern void MultiRecordFreeSpace(RelFileNode *rel,
|
extern void MultiRecordFreeSpace(RelFileNode *rel,
|
||||||
BlockNumber minPage,
|
BlockNumber minPage,
|
||||||
BlockNumber maxPage,
|
|
||||||
int nPages,
|
int nPages,
|
||||||
BlockNumber *pages,
|
PageFreeSpaceInfo *pageSpaces);
|
||||||
Size *spaceAvail);
|
|
||||||
extern void FreeSpaceMapForgetRel(RelFileNode *rel);
|
extern void FreeSpaceMapForgetRel(RelFileNode *rel);
|
||||||
extern void FreeSpaceMapForgetDatabase(Oid dbid);
|
extern void FreeSpaceMapForgetDatabase(Oid dbid);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user