vacuum.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,201 行 · 第 1/5 页
C
2,201 行
if (num_tuples == 0) min_tlen = max_tlen = 0; vacrelstats->min_tlen = min_tlen; vacrelstats->max_tlen = max_tlen; vacuum_pages->empty_end_pages = empty_end_pages; fraged_pages->empty_end_pages = empty_end_pages; /* * Clear the fraged_pages list if we found we couldn't shrink. Else, * remove any "empty" end-pages from the list, and compute usable free * space = free space in remaining pages. */ if (do_shrinking) { Assert((BlockNumber) fraged_pages->num_pages >= empty_end_pages); fraged_pages->num_pages -= empty_end_pages; usable_free_space = 0; for (i = 0; i < fraged_pages->num_pages; i++) usable_free_space += fraged_pages->pagedesc[i]->free; } else { fraged_pages->num_pages = 0; usable_free_space = 0; } /* don't bother to save vtlinks if we will not call repair_frag */ if (fraged_pages->num_pages > 0 && num_vtlinks > 0) { qsort((char *) vtlinks, num_vtlinks, sizeof(VTupleLinkData), vac_cmp_vtlinks); vacrelstats->vtlinks = vtlinks; vacrelstats->num_vtlinks = num_vtlinks; } else { vacrelstats->vtlinks = NULL; vacrelstats->num_vtlinks = 0; pfree(vtlinks); } ereport(elevel, (errmsg("\"%s\": found %.0f removable, %.0f nonremovable row versions in %u pages", RelationGetRelationName(onerel), tups_vacuumed, num_tuples, nblocks), errdetail("%.0f dead row versions cannot be removed yet.\n" "Nonremovable row versions range from %lu to %lu bytes long.\n" "There were %.0f unused item pointers.\n" "Total free space (including removable row versions) is %.0f bytes.\n" "%u pages are or will become empty, including %u at the end of the table.\n" "%u pages containing %.0f free bytes are potential move destinations.\n" "%s", nkeep, (unsigned long) min_tlen, (unsigned long) max_tlen, nunused, free_space, empty_pages, empty_end_pages, fraged_pages->num_pages, usable_free_space, vac_show_rusage(&ru0))));}/* * repair_frag() -- try to repair relation's fragmentation * * This routine marks dead tuples as unused and tries re-use dead space * by moving tuples (and inserting indexes if needed). It constructs * Nvacpagelist list of free-ed pages (moved tuples) and clean indexes * for them after committing (in hack-manner - without losing locks * and freeing memory!) current transaction. It truncates relation * if some end-blocks are gone away. */static voidrepair_frag(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages, VacPageList fraged_pages, int nindexes, Relation *Irel){ TransactionId myXID; CommandId myCID; Buffer buf, cur_buffer; BlockNumber nblocks, blkno; BlockNumber last_move_dest_block = 0, last_vacuum_block; Page page, ToPage = NULL; OffsetNumber offnum, maxoff, newoff, max_offset; ItemId itemid, newitemid; HeapTupleData tuple, newtup; TupleDesc tupdesc; ResultRelInfo *resultRelInfo; EState *estate; TupleTable tupleTable; TupleTableSlot *slot; VacPageListData Nvacpagelist; VacPage cur_page = NULL, last_vacuum_page, vacpage, *curpage; int cur_item = 0; int i; Size tuple_len; int num_moved, num_fraged_pages, vacuumed_pages; int checked_moved, num_tuples, keep_tuples = 0; bool isempty, dowrite, chain_tuple_moved; VacRUsage ru0; vac_init_rusage(&ru0); myXID = GetCurrentTransactionId(); myCID = GetCurrentCommandId(); tupdesc = RelationGetDescr(onerel); /* * We need a ResultRelInfo and an EState so we can use the regular * executor's index-entry-making machinery. */ estate = CreateExecutorState(); resultRelInfo = makeNode(ResultRelInfo); resultRelInfo->ri_RangeTableIndex = 1; /* dummy */ resultRelInfo->ri_RelationDesc = onerel; resultRelInfo->ri_TrigDesc = NULL; /* we don't fire triggers */ ExecOpenIndices(resultRelInfo); estate->es_result_relations = resultRelInfo; estate->es_num_result_relations = 1; estate->es_result_relation_info = resultRelInfo; /* Set up a dummy tuple table too */ tupleTable = ExecCreateTupleTable(1); slot = ExecAllocTableSlot(tupleTable); ExecSetSlotDescriptor(slot, tupdesc, false); Nvacpagelist.num_pages = 0; num_fraged_pages = fraged_pages->num_pages; Assert((BlockNumber) vacuum_pages->num_pages >= vacuum_pages->empty_end_pages); vacuumed_pages = vacuum_pages->num_pages - vacuum_pages->empty_end_pages; if (vacuumed_pages > 0) { /* get last reaped page from vacuum_pages */ last_vacuum_page = vacuum_pages->pagedesc[vacuumed_pages - 1]; last_vacuum_block = last_vacuum_page->blkno; } else { last_vacuum_page = NULL; last_vacuum_block = InvalidBlockNumber; } cur_buffer = InvalidBuffer; num_moved = 0; vacpage = (VacPage) palloc(sizeof(VacPageData) + MaxOffsetNumber * sizeof(OffsetNumber)); vacpage->offsets_used = vacpage->offsets_free = 0; /* * Scan pages backwards from the last nonempty page, trying to move * tuples down to lower pages. Quit when we reach a page that we have * moved any tuples onto, or the first page if we haven't moved * anything, or when we find a page we cannot completely empty (this * last condition is handled by "break" statements within the loop). * * NB: this code depends on the vacuum_pages and fraged_pages lists being * in order by blkno. */ nblocks = vacrelstats->rel_pages; for (blkno = nblocks - vacuum_pages->empty_end_pages - 1; blkno > last_move_dest_block; blkno--) { CHECK_FOR_INTERRUPTS(); /* * Forget fraged_pages pages at or after this one; they're no * longer useful as move targets, since we only want to move down. * Note that since we stop the outer loop at last_move_dest_block, * pages removed here cannot have had anything moved onto them * already. * * Also note that we don't change the stored fraged_pages list, only * our local variable num_fraged_pages; so the forgotten pages are * still available to be loaded into the free space map later. */ while (num_fraged_pages > 0 && fraged_pages->pagedesc[num_fraged_pages - 1]->blkno >= blkno) { Assert(fraged_pages->pagedesc[num_fraged_pages - 1]->offsets_used == 0); --num_fraged_pages; } /* * Process this page of relation. */ buf = ReadBuffer(onerel, blkno); page = BufferGetPage(buf); vacpage->offsets_free = 0; isempty = PageIsEmpty(page); dowrite = false; /* Is the page in the vacuum_pages list? */ if (blkno == last_vacuum_block) { if (last_vacuum_page->offsets_free > 0) { /* there are dead tuples on this page - clean them */ Assert(!isempty); LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); vacuum_page(onerel, buf, last_vacuum_page); LockBuffer(buf, BUFFER_LOCK_UNLOCK); dowrite = true; } else Assert(isempty); --vacuumed_pages; if (vacuumed_pages > 0) { /* get prev reaped page from vacuum_pages */ last_vacuum_page = vacuum_pages->pagedesc[vacuumed_pages - 1]; last_vacuum_block = last_vacuum_page->blkno; } else { last_vacuum_page = NULL; last_vacuum_block = InvalidBlockNumber; } if (isempty) { ReleaseBuffer(buf); continue; } } else Assert(!isempty); chain_tuple_moved = false; /* no one chain-tuple was moved * off this page, yet */ vacpage->blkno = blkno; maxoff = PageGetMaxOffsetNumber(page); for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum)) { itemid = PageGetItemId(page, offnum); if (!ItemIdIsUsed(itemid)) continue; tuple.t_datamcxt = NULL; tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid); tuple_len = tuple.t_len = ItemIdGetLength(itemid); ItemPointerSet(&(tuple.t_self), blkno, offnum); if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple.t_data->t_infomask & HEAP_MOVED_IN) elog(ERROR, "HEAP_MOVED_IN was not expected"); /* * If this (chain) tuple is moved by me already then I * have to check is it in vacpage or not - i.e. is it * moved while cleaning this page or some previous one. */ if (tuple.t_data->t_infomask & HEAP_MOVED_OFF) { if (HeapTupleHeaderGetXvac(tuple.t_data) != myXID) elog(ERROR, "invalid XVAC in tuple header"); if (keep_tuples == 0) continue; if (chain_tuple_moved) /* some chains was moved * while */ { /* cleaning this page */ Assert(vacpage->offsets_free > 0); for (i = 0; i < vacpage->offsets_free; i++) { if (vacpage->offsets[i] == offnum) break; } if (i >= vacpage->offsets_free) /* not found */ { vacpage->offsets[vacpage->offsets_free++] = offnum; keep_tuples--; } } else { vacpage->offsets[vacpage->offsets_free++] = offnum; keep_tuples--; } continue; } elog(ERROR, "HEAP_MOVED_OFF was expected"); } /* * If this tuple is in the chain of tuples created in updates * by "recent" transactions then we have to move all chain of * tuples to another places. * * NOTE: this test is not 100% accurate: it is possible for a * tuple to be an updated one with recent xmin, and yet not * have a corresponding tuple in the vtlinks list. Presumably * there was once a parent tuple with xmax matching the xmin, * but it's possible that that tuple has been removed --- for * example, if it had xmin = xmax then * HeapTupleSatisfiesVacuum would deem it removable as soon as * the xmin xact completes. * * To be on the safe side, we abandon the repair_frag process if * we cannot find the parent tuple in vtlinks. This may be * overly conservative; AFAICS it would be safe to move the * chain. */ if (((tuple.t_data->t_infomask & HEAP_UPDATED) && !TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data), OldestXmin)) || (!(tuple.t_data->t_infomask & (HEAP_XMAX_INVALID | HEAP_MARKED_FOR_UPDATE)) && !(ItemPointerEquals(&(tuple.t_self), &(tuple.t_data->t_ctid))))) { Buffer Cbuf = buf; bool freeCbuf = false; bool chain_move_failed = false; Page Cpage; ItemId Citemid; ItemPointerData Ctid; HeapTupleData tp = tuple; Size tlen = tuple_len; VTupleMove vtmove; int num_vtmove; int free_vtmove; VacPage to_vacpage = NULL; int to_item = 0; int ti; if (cur_buffer != InvalidBuffer) { WriteBuffer(cur_buffer); cur_buffer = InvalidBuffer; } /* Quick exit if we have no vtlinks to search in */ if (vacrelstats->vtlinks == NULL) { elog(DEBUG2, "parent item in update-chain not found --- can't continue repair_frag"); break; /* out of walk-along-page loop */ } vtmove = (VTupleMove) palloc(100 * sizeof(VTupleMoveData)); num_vtmove = 0; free_vtmove = 100; /* * If this tuple is in the begin/middle of the chain then * we have to move to the end of chain. */ while (!(tp.t_data->t_infomask & (HEAP_XMAX_INVALID | HEAP_MARKED_FOR_UPDATE)) && !(ItemPointerEquals(&(tp.t_self), &(tp.t_data->t_ctid)))) { Ctid = tp.t_data->t_ctid; if (freeCbuf) ReleaseBuffer(Cbuf); freeCbuf = true; Cbuf = ReadBuffer(onerel, ItemPointerGetBlockNumber(&Ctid)); Cpage = BufferGetPage(Cbuf); Citemid = PageGetItemId(Cpage, ItemPointerGetOffsetNumber(&Ctid)); if (!ItemIdIsUsed(Citemid)) { /* * This means that in the middle of chain there * was tuple updated by older (than OldestXmin) * xaction and this tuple is already deleted by * me. Actually, upper part of chain should be * removed and seems that this should be handled * in scan_heap(), but it's not implemented at the * moment and so we just stop shrinking here. */ elog(DEBUG2, "child itemid in update-chain marked as unused --- can't continue repair_frag"); chain_move_failed = true; break; /* out of loop to move to chain end */ } tp.t_datamcxt = NULL; tp.t_data = (HeapTupleHeader) PageGetItem(Cpage, Citemid); tp.t_self = Ctid; tlen = tp.t_len = ItemIdGetLength(Citemid); } if (chain_move_failed) { if (freeCbuf) ReleaseBuffer(Cbuf); pfree(vtmove); break; /* out of walk-along-page loop */ } /* * Check if all items in chain can be moved */ for (;;) { Buffer Pbuf; Page Ppage; ItemId Pitemid; HeapTupleData Ptp; VTupleLinkData vtld, *vtlp; if (to_vacpage == NULL || !enough_space(to_vacpage, tlen)) { for (i = 0; i < num_fraged_pages; i++) { if (enough_space(fraged_pages->pagedesc[i], tlen)) break; } if (i == num_fraged_pages) { /* can't move item anywhere */ chain_move_failed = true;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?