📄 vacuum.c
字号:
min_tlen = max_tlen = 0; vacrelstats->min_tlen = min_tlen; vacrelstats->max_tlen = max_tlen; vacuum_pages->vpl_empty_end_pages = empty_end_pages; fraged_pages->vpl_empty_end_pages = empty_end_pages; /* * Try to make fraged_pages keeping in mind that we can't use free * space of "empty" end-pages and last page if it reapped. */ if (do_shrinking && vacuum_pages->vpl_num_pages - empty_end_pages > 0) { int nusf; /* blocks usefull for re-using */ nusf = vacuum_pages->vpl_num_pages - empty_end_pages; if ((vacuum_pages->vpl_pagedesc[nusf - 1])->vpd_blkno == nblocks - empty_end_pages - 1) nusf--; for (i = 0; i < nusf; i++) { vp = vacuum_pages->vpl_pagedesc[i]; if (vc_enough_space(vp, min_tlen)) { vc_vpinsert(fraged_pages, vp); usable_free_size += vp->vpd_free; } } } if (usable_free_size > 0 && num_vtlinks > 0) { qsort((char *) vtlinks, num_vtlinks, sizeof(VTupleLinkData), vc_cmp_vtlinks); vacrelstats->vtlinks = vtlinks; vacrelstats->num_vtlinks = num_vtlinks; } else { vacrelstats->vtlinks = NULL; vacrelstats->num_vtlinks = 0; pfree(vtlinks); } getrusage(RUSAGE_SELF, &ru1); elog(MESSAGE_LEVEL, "Pages %u: Changed %u, Reapped %u, Empty %u, New %u; \Tup %u: Vac %u, Keep/VTL %u/%u, Crash %u, UnUsed %u, MinLen %u, MaxLen %u; \Re-using: Free/Avail. Space %u/%u; EndEmpty/Avail. Pages %u/%u. \Elapsed %u/%u sec.", nblocks, changed_pages, vacuum_pages->vpl_num_pages, empty_pages, new_pages, num_tuples, tups_vacuumed, nkeep, vacrelstats->num_vtlinks, ncrash, nunused, min_tlen, max_tlen, free_size, usable_free_size, empty_end_pages, fraged_pages->vpl_num_pages, ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec);} /* vc_scanheap *//* * vc_rpfheap() -- try to repaire relation' fragmentation * * This routine marks dead tuples as unused and tries re-use dead space * by moving tuples (and inserting indices if needed). It constructs * Nvpl list of free-ed pages (moved tuples) and clean indices * 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 voidvc_rpfheap(VRelStats *vacrelstats, Relation onerel, VPageList vacuum_pages, VPageList fraged_pages, int nindices, Relation *Irel){ TransactionId myXID; CommandId myCID; Buffer buf, cur_buffer; int nblocks, blkno; Page page, ToPage = NULL; OffsetNumber offnum = 0, maxoff = 0, newoff, max_offset; ItemId itemid, newitemid; HeapTupleData tuple, newtup; TupleDesc tupdesc = NULL; Datum *idatum = NULL; char *inulls = NULL; InsertIndexResult iresult; VPageListData Nvpl; VPageDescr cur_page = NULL, last_fraged_page, last_vacuum_page, vpc, *vpp; int cur_item = 0; IndDesc *Idesc, *idcur; int last_fraged_block, last_vacuum_block, i = 0; 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; struct rusage ru0, ru1; getrusage(RUSAGE_SELF, &ru0); myXID = GetCurrentTransactionId(); myCID = GetCurrentCommandId(); if (Irel != (Relation *) NULL) /* preparation for index' inserts */ { vc_mkindesc(onerel, nindices, Irel, &Idesc); tupdesc = RelationGetDescr(onerel); idatum = (Datum *) palloc(INDEX_MAX_KEYS * sizeof(*idatum)); inulls = (char *) palloc(INDEX_MAX_KEYS * sizeof(*inulls)); } Nvpl.vpl_num_pages = 0; num_fraged_pages = fraged_pages->vpl_num_pages; last_fraged_page = fraged_pages->vpl_pagedesc[num_fraged_pages - 1]; last_fraged_block = last_fraged_page->vpd_blkno; Assert(vacuum_pages->vpl_num_pages > vacuum_pages->vpl_empty_end_pages); vacuumed_pages = vacuum_pages->vpl_num_pages - vacuum_pages->vpl_empty_end_pages; last_vacuum_page = vacuum_pages->vpl_pagedesc[vacuumed_pages - 1]; last_vacuum_block = last_vacuum_page->vpd_blkno; Assert(last_vacuum_block >= last_fraged_block); cur_buffer = InvalidBuffer; num_moved = 0; vpc = (VPageDescr) palloc(sizeof(VPageDescrData) + MaxOffsetNumber * sizeof(OffsetNumber)); vpc->vpd_offsets_used = vpc->vpd_offsets_free = 0; nblocks = vacrelstats->num_pages; for (blkno = nblocks - vacuum_pages->vpl_empty_end_pages - 1;; blkno--) { /* if it's reapped page and it was used by me - quit */ if (blkno == last_fraged_block && last_fraged_page->vpd_offsets_used > 0) break; buf = ReadBuffer(onerel, blkno); page = BufferGetPage(buf); vpc->vpd_offsets_free = 0; isempty = PageIsEmpty(page); dowrite = false; if (blkno == last_vacuum_block) /* it's reapped page */ { if (last_vacuum_page->vpd_offsets_free > 0) /* there are dead tuples */ { /* on this page - clean */ Assert(!isempty); vc_vacpage(page, last_vacuum_page); dowrite = true; } else Assert(isempty); --vacuumed_pages; Assert(vacuumed_pages > 0); /* get prev reapped page from vacuum_pages */ last_vacuum_page = vacuum_pages->vpl_pagedesc[vacuumed_pages - 1]; last_vacuum_block = last_vacuum_page->vpd_blkno; if (blkno == last_fraged_block) /* this page in * fraged_pages too */ { --num_fraged_pages; Assert(num_fraged_pages > 0); Assert(last_fraged_page->vpd_offsets_used == 0); /* get prev reapped page from fraged_pages */ last_fraged_page = fraged_pages->vpl_pagedesc[num_fraged_pages - 1]; last_fraged_block = last_fraged_page->vpd_blkno; } Assert(last_fraged_block <= last_vacuum_block); if (isempty) { ReleaseBuffer(buf); continue; } } else Assert(!isempty); chain_tuple_moved = false; /* no one chain-tuple was moved * off this page, yet */ vpc->vpd_blkno = blkno; maxoff = PageGetMaxOffsetNumber(page); for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum)) { itemid = PageGetItemId(page, offnum); if (!ItemIdIsUsed(itemid)) continue; 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 ((TransactionId) tuple.t_data->t_cmin != myXID) elog(ERROR, "Invalid XID in t_cmin"); 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 vpc 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 (keep_tuples == 0) continue; if (chain_tuple_moved) /* some chains was moved * while */ { /* cleaning this page */ Assert(vpc->vpd_offsets_free > 0); for (i = 0; i < vpc->vpd_offsets_free; i++) { if (vpc->vpd_offsets[i] == offnum) break; } if (i >= vpc->vpd_offsets_free) /* not found */ { vpc->vpd_offsets[vpc->vpd_offsets_free++] = offnum; keep_tuples--; } } else { vpc->vpd_offsets[vpc->vpd_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. */ if ((tuple.t_data->t_infomask & HEAP_UPDATED && tuple.t_data->t_xmin >= XmaxRecent) || (!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID) && !(ItemPointerEquals(&(tuple.t_self), &(tuple.t_data->t_ctid))))) { Buffer Cbuf = buf; Page Cpage; ItemId Citemid; ItemPointerData Ctid; HeapTupleData tp = tuple; Size tlen = tuple_len; VTupleMove vtmove = (VTupleMove) palloc(100 * sizeof(VTupleMoveData)); int num_vtmove = 0; int free_vtmove = 100; VPageDescr to_vpd = fraged_pages->vpl_pagedesc[0]; int to_item = 0; bool freeCbuf = false; int ti; if (vacrelstats->vtlinks == NULL) elog(ERROR, "No one parent tuple was found"); if (cur_buffer != InvalidBuffer) { WriteBuffer(cur_buffer); cur_buffer = InvalidBuffer; } /* * 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) && !(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 XmaxRecent) 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 vc_scanheap(), but * it's not implemented at the moment and so we * just stop shrinking here. */ ReleaseBuffer(Cbuf); pfree(vtmove); vtmove = NULL; elog(NOTICE, "Child itemid in update-chain marked as unused - can't continue vc_rpfheap"); break; } tp.t_data = (HeapTupleHeader) PageGetItem(Cpage, Citemid); tp.t_self = Ctid; tlen = tp.t_len = ItemIdGetLength(Citemid); } if (vtmove == NULL) break; /* first, can chain be moved ? */ for (;;) { if (!vc_enough_space(to_vpd, tlen)) { if (to_vpd != last_fraged_page && !vc_enough_space(to_vpd, vacrelstats->min_tlen)) { Assert(num_fraged_pages > to_item + 1); memmove(fraged_pages->vpl_pagedesc + to_item, fraged_pages->vpl_pagedesc + to_item + 1, sizeof(VPageDescr *) * (num_fraged_pages - to_item - 1)); num_fraged_pages--; Assert(last_fraged_page == fraged_pages->vpl_pagedesc[num_fraged_pages - 1]); } for (i = 0; i < num_fraged_pages; i++) { if (vc_enough_space(fraged_pages->vpl_pagedesc[i], tlen)) break; } if (i == num_fraged_pages) /* can't move item * anywhere */ { for (i = 0; i < num_vtmove; i++) { Assert(vtmove[i].vpd->vpd_offsets_used > 0); (vtmove[i].vpd->vpd_offsets_used)--; } num_vtmove = 0; break; } to_item = i; to_vpd = fraged_pages->vpl_pagedesc[to_item]; } to_vpd->vpd_free -= MAXALIGN(tlen); if (to_vpd->vpd_offsets_used >= to_vpd->vpd_offsets_free) to_vpd->vpd_free -= MAXALIGN(sizeof(ItemIdData)); (to_vpd->vpd_offsets_used)++; if (free_vtmove == 0) { free_vtmove = 1000; vtmove = (VTupleMove) repalloc(vtmove, (free_vtmove + num_vtmove) * sizeof(VTupleMoveData)); } vtmove[num_vtmove].tid = tp.t_self; vtmove[num_vtmove].vpd = to_vpd; if (to_vpd->vpd_offsets_used == 1) vtmove[num_vtmove].cleanVpd = true; else vtmove[num_vtmove].cleanVpd = false; free_vtmove--; num_vtmove++; /* * All done ? */ if (!(tp.t_data->t_infomask & HEAP_UPDATED) || tp.t_data->t_xmin < XmaxRecent) break; /* * Well, try to find tuple with old row version */ for (;;) { Buffer Pbuf; Page Ppage; ItemId Pitemid; HeapTupleData Ptp; VTupleLinkData vtld, *vtlp; vtld.new_tid = tp.t_self; vtlp = (VTupleLink) vc_find_eq((void *) (vacrelstats->vtlinks), vacrelstats->num_vtlinks, sizeof(VTupleLinkData), (void *) &vtld, vc_cmp_vtlinks); if (vtlp == NULL) elog(ERROR, "Parent tuple was not found"); tp.t_self = vtlp->this_tid; Pbuf = ReadBuffer(onerel, ItemPointerGetBlockNumber(&(tp.t_self))); Ppage = BufferGetPage(Pbuf); Pitemid = PageGetItemId(Ppage, ItemPointerGetOffsetNumber(&(tp.t_self))); if (!ItemIdIsUsed(Pitemid)) elog(ERROR, "Parent itemid marked as unused"); Ptp.t_data = (HeapTupleHeader) PageGetItem(Ppage, Pitemid); Assert(ItemPointerEquals(&(vtld.new_tid), &(Ptp.t_data->t_ctid))); /* * Read above about cases when !ItemIdIsUsed(Citemid) * (child item is removed)... Due to the fact that * at the moment we don't remove unuseful part of * update-chain, it's possible to get too old * parent row here. Like as in the case which * caused this problem, we stop shrinking here. * I could try to find real parent row but want * not to do it because of real solution will * be implemented anyway, latter, and we are too * close to 6.5 release. - vadim 06/11/99 */ if (Ptp.t_data->t_xmax != tp.t_data->t_xmin) { if (freeCbuf) ReleaseBuffer(Cbuf); freeCbuf = false; ReleaseBuffer(Pbuf); for (i = 0; i < num_vtmove; i++) { Assert(vtmove[i].vpd->vpd_offsets_used > 0); (vtmove[i].vpd->vpd_offsets_used)--; } num_vtmove = 0; elog(NOTICE, "Too old parent tuple found - can't continue vc_rpfheap"); break; }#ifdef NOT_USED /* I'm not sure that this will wotk properly... */ /* * If this tuple is updated version of row and it * was created by the same transaction then no one * is interested in this tuple - mark it as * removed. */ if (Ptp.t_data->t_infomask & HEAP_UPDATED && Ptp.t_data->t_xmin == Ptp.t_data->t_xmax) { TransactionIdStore(myXID, (TransactionId *) &(Ptp.t_data->t_cmin)); Ptp.t_data->t_infomask &=
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -