PostgreSQL的vacuum過程中heap_vacuum_rel函數(shù)分析

這篇文章主要介紹“PostgreSQL的vacuum過程中heap_vacuum_rel函數(shù)分析”,在日常操作中,相信很多人在PostgreSQL的vacuum過程中heap_vacuum_rel函數(shù)分析問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”PostgreSQL的vacuum過程中heap_vacuum_rel函數(shù)分析”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

隨縣網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián),隨縣網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為隨縣超過千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的隨縣做網(wǎng)站的公司定做!

本節(jié)簡單介紹了PostgreSQL手工執(zhí)行vacuum的實(shí)現(xiàn)邏輯,主要分析了ExecVacuum->vacuum->vacuum_rel->heap_vacuum_rel函數(shù)的實(shí)現(xiàn)邏輯。

一、數(shù)據(jù)結(jié)構(gòu)

宏定義
Vacuum和Analyze命令選項

/* ----------------------
 *      Vacuum and Analyze Statements
 *      Vacuum和Analyze命令選項
 * 
 * Even though these are nominally two statements, it's convenient to use
 * just one node type for both.  Note that at least one of VACOPT_VACUUM
 * and VACOPT_ANALYZE must be set in options.
 * 雖然在這里有兩種不同的語句,但只需要使用統(tǒng)一的Node類型即可.
 * 注意至少VACOPT_VACUUM/VACOPT_ANALYZE在選項中設(shè)置.
 * ----------------------
 */
typedef enum VacuumOption
{
    VACOPT_VACUUM = 1 << 0,     /* do VACUUM */
    VACOPT_ANALYZE = 1 << 1,    /* do ANALYZE */
    VACOPT_VERBOSE = 1 << 2,    /* print progress info */
    VACOPT_FREEZE = 1 << 3,     /* FREEZE option */
    VACOPT_FULL = 1 << 4,       /* FULL (non-concurrent) vacuum */
    VACOPT_SKIP_LOCKED = 1 << 5,    /* skip if cannot get lock */
    VACOPT_SKIPTOAST = 1 << 6,  /* don't process the TOAST table, if any */
    VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7   /* don't skip any pages */
} VacuumOption;

VacuumStmt
存儲vacuum命令的option&Relation鏈表

typedef struct VacuumStmt
{
    NodeTag     type;//Tag
    //VacuumOption位標(biāo)記
    int         options;        /* OR of VacuumOption flags */
    //VacuumRelation鏈表,如為NIL-->所有Relation.
    List       *rels;           /* list of VacuumRelation, or NIL for all */
} VacuumStmt;

VacuumParams
vacuum命令參數(shù)

/*
 * Parameters customizing behavior of VACUUM and ANALYZE.
 * 客戶端調(diào)用VACUUM/ANALYZE時的定制化參數(shù)
 */
typedef struct VacuumParams
{
    //最小freeze age,-1表示使用默認(rèn)
    int         freeze_min_age; /* min freeze age, -1 to use default */
    //掃描整個table的freeze age
    int         freeze_table_age;   /* age at which to scan whole table */
    //最小的multixact freeze age,-1表示默認(rèn)
    int         multixact_freeze_min_age;   /* min multixact freeze age, -1 to
                                             * use default */
    //掃描全表的freeze age,-1表示默認(rèn)
    int         multixact_freeze_table_age; /* multixact age at which to scan
                                             * whole table */
    //是否強(qiáng)制wraparound?
    bool        is_wraparound;  /* force a for-wraparound vacuum */
    //以毫秒為單位的最小執(zhí)行閾值
    int         log_min_duration;   /* minimum execution threshold in ms at
                                     * which  verbose logs are activated, -1
                                     * to use default */
} VacuumParams;

VacuumRelation
VACUUM/ANALYZE命令的目標(biāo)表信息

/*
 * Info about a single target table of VACUUM/ANALYZE.
 * VACUUM/ANALYZE命令的目標(biāo)表信息.
 *  
 * If the OID field is set, it always identifies the table to process.
 * Then the relation field can be NULL; if it isn't, it's used only to report
 * failure to open/lock the relation.
 * 如設(shè)置了OID字段,該值通常是將要處理的數(shù)據(jù)表.
 * 那么關(guān)系字段可以為空;如果不是,則僅用于報告未能打開/鎖定關(guān)系。
 */
typedef struct VacuumRelation
{
    NodeTag     type;
    RangeVar   *relation;       /* table name to process, or NULL */
    Oid         oid;            /* table's OID; InvalidOid if not looked up */
    List       *va_cols;        /* list of column names, or NIL for all */
} VacuumRelation;

BufferAccessStrategy
Buffer訪問策略對象

/*
 * Buffer identifiers.
 * Buffer標(biāo)識符
 * 
 * Zero is invalid, positive is the index of a shared buffer (1..NBuffers),
 * negative is the index of a local buffer (-1 .. -NLocBuffer).
 * 0表示無效,正整數(shù)表示共享buffer的索引(1..N),
 *   負(fù)數(shù)是本地buffer的索引(-1..-N)
 */
typedef int Buffer;
#define InvalidBuffer   0
/*
 * Buffer access strategy objects.
 * Buffer訪問策略對象
 *
 * BufferAccessStrategyData is private to freelist.c
 * BufferAccessStrategyData對freelist.c來說是私有的
 */
typedef struct BufferAccessStrategyData *BufferAccessStrategy;
 /*
 * Private (non-shared) state for managing a ring of shared buffers to re-use.
 * This is currently the only kind of BufferAccessStrategy object, but someday
 * we might have more kinds.
 * 私有狀態(tài),用于管理可重用的環(huán)形緩沖區(qū).
 * 目前只有這么一種緩沖區(qū)訪問策略對象,但未來某一天可以擁有更多.
 */
typedef struct BufferAccessStrategyData
{
    /* Overall strategy type */
    //全局的策略類型
    BufferAccessStrategyType btype;
    /* Number of elements in buffers[] array */
    //buffers[]中的元素個數(shù)
    int         ring_size;
    /*
     * Index of the "current" slot in the ring, ie, the one most recently
     * returned by GetBufferFromRing.
     * 環(huán)形緩沖區(qū)中當(dāng)前slot的索引,最近訪問的通過函數(shù)GetBufferFromRing返回.
     */
    int         current;
    /*
     * True if the buffer just returned by StrategyGetBuffer had been in the
     * ring already.
     * 如正好通過StrategyGetBuffer返回的buffer已在環(huán)形緩沖區(qū)中,則返回T
     */
    bool        current_was_in_ring;
    /*
     * Array of buffer numbers.  InvalidBuffer (that is, zero) indicates we
     * have not yet selected a buffer for this ring slot.  For allocation
     * simplicity this is palloc'd together with the fixed fields of the
     * struct.
     * buffer編號數(shù)組.
     * InvalidBuffer(即:0)表示我們還沒有為該slot選擇buffer.
     * 為了分配的簡單性,這是palloc'd與結(jié)構(gòu)的固定字段。
     */
    Buffer      buffers[FLEXIBLE_ARRAY_MEMBER];
}           BufferAccessStrategyData;
//Block結(jié)構(gòu)體指針
typedef void *Block;
/* Possible arguments for GetAccessStrategy() */
//GetAccessStrategy()函數(shù)可取值的參數(shù)
typedef enum BufferAccessStrategyType
{
    //常規(guī)的隨機(jī)訪問
    BAS_NORMAL,                 /* Normal random access */
    //大規(guī)模的只讀掃描
    BAS_BULKREAD,               /* Large read-only scan (hint bit updates are
                                 * ok) */
    //大量的多塊寫(如 COPY IN)
    BAS_BULKWRITE,              /* Large multi-block write (e.g. COPY IN) */
    //VACUUM
    BAS_VACUUM                  /* VACUUM */
} BufferAccessStrategyType;

LVRelStats

typedef struct LVRelStats
{
    /* hasindex = true means two-pass strategy; false means one-pass */
    //T表示two-pass strategy,F表示one-pass strategy
    bool        hasindex;
    /* Overall statistics about rel */
    //rel的全局統(tǒng)計信息
    //pg_class.relpages的上一個值
    BlockNumber old_rel_pages;  /* previous value of pg_class.relpages */
    //pages的總數(shù)
    BlockNumber rel_pages;      /* total number of pages */
    //掃描的pages
    BlockNumber scanned_pages;  /* number of pages we examined */
    //由于pin跳過的pages
    BlockNumber pinskipped_pages;   /* # of pages we skipped due to a pin */
    //跳過的frozen pages
    BlockNumber frozenskipped_pages;    /* # of frozen pages we skipped */
    //計算其元組的pages
    BlockNumber tupcount_pages; /* pages whose tuples we counted */
    //pg_class.reltuples的前值
    double      old_live_tuples;    /* previous value of pg_class.reltuples */
    //新估算的總元組數(shù)
    double      new_rel_tuples; /* new estimated total # of tuples */
    //新估算的存活元組數(shù)
    double      new_live_tuples;    /* new estimated total # of live tuples */
    //新估算的廢棄元組數(shù)
    double      new_dead_tuples;    /* new estimated total # of dead tuples */
    //已清除的pages
    BlockNumber pages_removed;
    //已刪除的tuples
    double      tuples_deleted;
    //實(shí)際上是非空page + 1
    BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */
    /* List of TIDs of tuples we intend to delete */
    /* NB: this list is ordered by TID address */
    //將要刪除的元組TIDs鏈表
    //注意:該鏈表已使用TID地址排序
    //當(dāng)前的入口/條目數(shù)
    int         num_dead_tuples;    /* current # of entries */
    //數(shù)組中已分配的slots(最大已廢棄元組數(shù))
    int         max_dead_tuples;    /* # slots allocated in array */
    //ItemPointerData數(shù)組
    ItemPointer dead_tuples;    /* array of ItemPointerData */
    //掃描的索引數(shù)
    int         num_index_scans;
    //最后被清除的事務(wù)ID
    TransactionId latestRemovedXid;
    //是否存在waiter?
    bool        lock_waiter_detected;
} LVRelStats;

PGRUsage
pg_rusage_init/pg_rusage_show的狀態(tài)結(jié)構(gòu)體

/* State structure for pg_rusage_init/pg_rusage_show */
//pg_rusage_init/pg_rusage_show的狀態(tài)結(jié)構(gòu)體
typedef struct PGRUsage
{
    struct timeval tv;
    struct rusage ru;
} PGRUsage;
struct rusage
{
    struct timeval ru_utime;    /* user time used */
    struct timeval ru_stime;    /* system time used */
};
struct timeval
{
__time_t tv_sec;        /* 秒數(shù).Seconds. */
__suseconds_t tv_usec;  /* 微秒數(shù).Microseconds-->這個英文注釋有問題. */
};

二、源碼解讀

heap_vacuum_rel() — 為heap relation執(zhí)行VACUUM
大體邏輯如下:
1.初始化相關(guān)變量,如本地變量/日志記錄級別/訪問策略等
2.調(diào)用vacuum_set_xid_limits計算最老的xmin和凍結(jié)截止點(diǎn)
3.判斷是否執(zhí)行全表(不跳過pages)掃描,標(biāo)記變量為aggressive
4.初始化統(tǒng)計信息結(jié)構(gòu)體vacrelstats
5.打開索引,執(zhí)行函數(shù)lazy_scan_heap進(jìn)行vacuuming,關(guān)閉索引
6.更新pg_class中的統(tǒng)計信息
7.收尾工作

/*
 *  heap_vacuum_rel() -- perform VACUUM for one heap relation
 *  heap_vacuum_rel() -- 為heap relation執(zhí)行VACUUM
 * 
 *      This routine vacuums a single heap, cleans out its indexes, and
 *      updates its relpages and reltuples statistics.
 *      該處理過程vacuum一個單獨(dú)的heap,清除索引并更新relpages和reltuples統(tǒng)計信息.
 *
 *      At entry, we have already established a transaction and opened
 *      and locked the relation.
 *      在該調(diào)用入口,我們已經(jīng)給創(chuàng)建了事務(wù)并且已經(jīng)打開&鎖定了relation.
 */
void
heap_vacuum_rel(Relation onerel, int options, VacuumParams *params,
                BufferAccessStrategy bstrategy)
{
    LVRelStats *vacrelstats;//統(tǒng)計信息
    Relation   *Irel;//關(guān)系指針
    int         nindexes;
    PGRUsage    ru0;//狀態(tài)結(jié)構(gòu)體
    TimestampTz starttime = 0;//時間戳
    long        secs;//秒數(shù)
    int         usecs;//微秒數(shù)
    double      read_rate,//讀比率
                write_rate;//寫比率
    //是否掃描所有未凍結(jié)的pages?
    bool        aggressive;     /* should we scan all unfrozen pages? */
    //實(shí)際上是否掃描了所有這樣的pages?
    bool        scanned_all_unfrozen;   /* actually scanned all such pages? */
    TransactionId xidFullScanLimit;
    MultiXactId mxactFullScanLimit;
    BlockNumber new_rel_pages;
    BlockNumber new_rel_allvisible;
    double      new_live_tuples;
    TransactionId new_frozen_xid;
    MultiXactId new_min_multi;
    Assert(params != NULL);
    /* measure elapsed time iff autovacuum logging requires it */
    //如autovacuum日志記錄需要,則測量耗費(fèi)的時間
    if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
    {
        pg_rusage_init(&ru0);
        starttime = GetCurrentTimestamp();
    }
    if (options & VACOPT_VERBOSE)
        //需要VERBOSE
        elevel = INFO;
    else
        elevel = DEBUG2;
    pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM,
                                  RelationGetRelid(onerel));
    vac_strategy = bstrategy;
    //計算最老的xmin和凍結(jié)截止點(diǎn)
    //輸出:OldestXmin/FreezeLimit/FreezeLimit/MultiXactCutoff/mxactFullScanLimit
    vacuum_set_xid_limits(onerel,
                          params->freeze_min_age,
                          params->freeze_table_age,
                          params->multixact_freeze_min_age,
                          params->multixact_freeze_table_age,
                          &OldestXmin, &FreezeLimit, &xidFullScanLimit,
                          &MultiXactCutoff, &mxactFullScanLimit);
    /*
     * We request an aggressive scan if the table's frozen Xid is now older
     * than or equal to the requested Xid full-table scan limit; or if the
     * table's minimum MultiXactId is older than or equal to the requested
     * mxid full-table scan limit; or if DISABLE_PAGE_SKIPPING was specified.
     * 如果表的frozen Xid現(xiàn)在大于或等于請求的Xid全表掃描限制,則請求進(jìn)行主動掃描;
     * 或者如果表的最小MultiXactId大于或等于請求的mxid全表掃描限制;
     * 或者,如果指定了disable_page_skip。
     */
    //比較onerel->rd_rel->relfrozenxid & xidFullScanLimit
    //如小于等于,則aggressive為T,否則為F
    aggressive = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,
                                               xidFullScanLimit);
    //比較onerel->rd_rel->relminmxid &mxactFullScanLimit
    //如小于等于,則aggressive為T
    //否則aggressive原值為T,則為T,否則為F
    aggressive |= MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid,
                                              mxactFullScanLimit);
    if (options & VACOPT_DISABLE_PAGE_SKIPPING)
        //禁用跳過頁,則強(qiáng)制為T
        aggressive = true;
    //分配統(tǒng)計結(jié)構(gòu)體內(nèi)存
    vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
    //記錄統(tǒng)計信息
    vacrelstats->old_rel_pages = onerel->rd_rel->relpages;
    vacrelstats->old_live_tuples = onerel->rd_rel->reltuples;
    vacrelstats->num_index_scans = 0;
    vacrelstats->pages_removed = 0;
    vacrelstats->lock_waiter_detected = false;
    /* Open all indexes of the relation */
    //打開該relation所有的索引
    vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel);
    vacrelstats->hasindex = (nindexes > 0);
    /* Do the vacuuming */
    //執(zhí)行vacuuming
    lazy_scan_heap(onerel, options, vacrelstats, Irel, nindexes, aggressive);
    /* Done with indexes */
    //已完成index的處理
    vac_close_indexes(nindexes, Irel, NoLock);
    /*
     * Compute whether we actually scanned the all unfrozen pages. If we did,
     * we can adjust relfrozenxid and relminmxid.
     * 計算我們實(shí)際上是否掃描了所有unfrozen pages.
     * 如果掃描了,則需要調(diào)整relfrozenxid和relminmxid.
     *
     * NB: We need to check this before truncating the relation, because that
     * will change ->rel_pages.
     * 注意:我們需要在截斷relation前執(zhí)行檢查,因?yàn)檫@會改變rel_pages.
     */
    if ((vacrelstats->scanned_pages + vacrelstats->frozenskipped_pages)
        < vacrelstats->rel_pages)
    {
        Assert(!aggressive);
        scanned_all_unfrozen = false;
    }
    else
        //掃描pages + 凍結(jié)跳過的pages >= 總pages,則為T
        scanned_all_unfrozen = true;
    /*
     * Optionally truncate the relation.
     * 可選的,截斷relation
     */
    if (should_attempt_truncation(vacrelstats))
        lazy_truncate_heap(onerel, vacrelstats);
    /* Report that we are now doing final cleanup */
    //通知其他進(jìn)程,正在進(jìn)行最后的清理
    pgstat_progress_update_param(PROGRESS_VACUUM_PHASE,
                                 PROGRESS_VACUUM_PHASE_FINAL_CLEANUP);
    /*
     * Update statistics in pg_class.
     * 更新pg_class中的統(tǒng)計信息.
     *
     * A corner case here is that if we scanned no pages at all because every
     * page is all-visible, we should not update relpages/reltuples, because
     * we have no new information to contribute.  In particular this keeps us
     * from replacing relpages=reltuples=0 (which means "unknown tuple
     * density") with nonzero relpages and reltuples=0 (which means "zero
     * tuple density") unless there's some actual evidence for the latter.
     * 這里的一個極端情況是,如果每個頁面都是可見的,這時候根本沒有掃描任何頁面,
     *   那么就不應(yīng)該更新relpages/reltuples,因?yàn)槲覀儧]有新信息可以更新。
     * 特別地,這阻止我們將relpages=reltuple =0(這意味著“未知的元組密度”)替換
     *   為非零relpages和reltuple=0(這意味著“零元組密度”),
     *   除非有關(guān)于后者的一些實(shí)際的證據(jù)。
     *
     * It's important that we use tupcount_pages and not scanned_pages for the
     * check described above; scanned_pages counts pages where we could not
     * get cleanup lock, and which were processed only for frozenxid purposes.
     * 對于上面描述的檢查,使用tupcount_pages而不是scanned_pages是很重要的;
     * scanned_pages對無法獲得清理鎖的頁面進(jìn)行計數(shù),這些頁面僅用于frozenxid目的。
     *
     * We do update relallvisible even in the corner case, since if the table
     * is all-visible we'd definitely like to know that.  But clamp the value
     * to be not more than what we're setting relpages to.
     * 即使在極端情況下,我們也會更新relallvisible,因?yàn)槿绻硎莂ll-visible的,那我們肯定想知道這個.
     * 但是不要超過我們設(shè)置relpages的值。
     *
     * Also, don't change relfrozenxid/relminmxid if we skipped any pages,
     * since then we don't know for certain that all tuples have a newer xmin.
     * 同時,如果我們跳過了所有的頁面,不能更新relfrozenxid/relminmxid,
     *   因?yàn)閺哪菚r起,我們不能確定所有元組是否都有更新的xmin.
     */
    new_rel_pages = vacrelstats->rel_pages;
    new_live_tuples = vacrelstats->new_live_tuples;
    if (vacrelstats->tupcount_pages == 0 && new_rel_pages > 0)
    {
        //實(shí)際處理的tuple為0而且總頁面不為0,則調(diào)整回原頁數(shù)
        new_rel_pages = vacrelstats->old_rel_pages;
        new_live_tuples = vacrelstats->old_live_tuples;
    }
    visibilitymap_count(onerel, &new_rel_allvisible, NULL);
    if (new_rel_allvisible > new_rel_pages)
        new_rel_allvisible = new_rel_pages;
    new_frozen_xid = scanned_all_unfrozen ? FreezeLimit : InvalidTransactionId;
    new_min_multi = scanned_all_unfrozen ? MultiXactCutoff : InvalidMultiXactId;
    //更新pg_class中的統(tǒng)計信息
    vac_update_relstats(onerel,
                        new_rel_pages,
                        new_live_tuples,
                        new_rel_allvisible,
                        vacrelstats->hasindex,
                        new_frozen_xid,
                        new_min_multi,
                        false);
    /* report results to the stats collector, too */
    //同時,發(fā)送結(jié)果給統(tǒng)計收集器
    pgstat_report_vacuum(RelationGetRelid(onerel),
                         onerel->rd_rel->relisshared,
                         new_live_tuples,
                         vacrelstats->new_dead_tuples);
    pgstat_progress_end_command();
    /* and log the action if appropriate */
    //并在適當(dāng)?shù)那闆r下記錄操作
    if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
    {
        //autovacuum && 參數(shù)log_min_duration >= 0
        TimestampTz endtime = GetCurrentTimestamp();
        if (params->log_min_duration == 0 ||
            TimestampDifferenceExceeds(starttime, endtime,
                                       params->log_min_duration))
        {
            StringInfoData buf;
            char       *msgfmt;
            TimestampDifference(starttime, endtime, &secs, &usecs);
            read_rate = 0;
            write_rate = 0;
            if ((secs > 0) || (usecs > 0))
            {
                read_rate = (double) BLCKSZ * VacuumPageMiss / (1024 * 1024) /
                    (secs + usecs / 1000000.0);
                write_rate = (double) BLCKSZ * VacuumPageDirty / (1024 * 1024) /
                    (secs + usecs / 1000000.0);
            }
            /*
             * This is pretty messy, but we split it up so that we can skip
             * emitting individual parts of the message when not applicable.
             */
            initStringInfo(&buf);
            if (params->is_wraparound)
            {
                if (aggressive)
                    msgfmt = _("automatic aggressive vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n");
                else
                    msgfmt = _("automatic vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n");
            }
            else
            {
                if (aggressive)
                    msgfmt = _("automatic aggressive vacuum of table \"%s.%s.%s\": index scans: %d\n");
                else
                    msgfmt = _("automatic vacuum of table \"%s.%s.%s\": index scans: %d\n");
            }
            appendStringInfo(&buf, msgfmt,
                             get_database_name(MyDatabaseId),
                             get_namespace_name(RelationGetNamespace(onerel)),
                             RelationGetRelationName(onerel),
                             vacrelstats->num_index_scans);
            appendStringInfo(&buf, _("pages: %u removed, %u remain, %u skipped due to pins, %u skipped frozen\n"),
                             vacrelstats->pages_removed,
                             vacrelstats->rel_pages,
                             vacrelstats->pinskipped_pages,
                             vacrelstats->frozenskipped_pages);
            appendStringInfo(&buf,
                             _("tuples: %.0f removed, %.0f remain, %.0f are dead but not yet removable, oldest xmin: %u\n"),
                             vacrelstats->tuples_deleted,
                             vacrelstats->new_rel_tuples,
                             vacrelstats->new_dead_tuples,
                             OldestXmin);
            appendStringInfo(&buf,
                             _("buffer usage: %d hits, %d misses, %d dirtied\n"),
                             VacuumPageHit,
                             VacuumPageMiss,
                             VacuumPageDirty);
            appendStringInfo(&buf, _("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n"),
                             read_rate, write_rate);
            appendStringInfo(&buf, _("system usage: %s"), pg_rusage_show(&ru0));
            ereport(LOG,
                    (errmsg_internal("%s", buf.data)));
            pfree(buf.data);
        }
    }
}

三、跟蹤分析

測試腳本

11:45:37 (xdb@[local]:5432)testdb=# vacuum t1;

啟動gdb,設(shè)置斷點(diǎn)
注:PG主線函數(shù)名稱已改為heap_vacuum_rel,PG 11.1仍為lazy_vacuum_rel

(gdb) c
Continuing.
Breakpoint 1, lazy_vacuum_rel (onerel=0x7f226cd9e9a0, options=1, params=0x7ffe010d5b70, bstrategy=0x1da9708)
    at vacuumlazy.c:197
197     TimestampTz starttime = 0;
(gdb)

輸入?yún)?shù)
relation

(gdb) p *onerel
$1 = {rd_node = {spcNode = 1663, dbNode = 16402, relNode = 50820}, rd_smgr = 0x0, rd_refcnt = 1, rd_backend = -1, 
  rd_islocaltemp = false, rd_isnailed = false, rd_isvalid = true, rd_indexvalid = 0 '\000', rd_statvalid = false, 
  rd_createSubid = 0, rd_newRelfilenodeSubid = 0, rd_rel = 0x7f226cd9ebb8, rd_att = 0x7f226cd9ecd0, rd_id = 50820, 
  rd_lockInfo = {lockRelId = {relId = 50820, dbId = 16402}}, rd_rules = 0x0, rd_rulescxt = 0x0, trigdesc = 0x0, 
  rd_rsdesc = 0x0, rd_fkeylist = 0x0, rd_fkeyvalid = false, rd_partkeycxt = 0x0, rd_partkey = 0x0, rd_pdcxt = 0x0, 
  rd_partdesc = 0x0, rd_partcheck = 0x0, rd_indexlist = 0x0, rd_oidindex = 0, rd_pkindex = 0, rd_replidindex = 0, 
  rd_statlist = 0x0, rd_indexattr = 0x0, rd_projindexattr = 0x0, rd_keyattr = 0x0, rd_pkattr = 0x0, rd_idattr = 0x0, 
  rd_projidx = 0x0, rd_pubactions = 0x0, rd_options = 0x0, rd_index = 0x0, rd_indextuple = 0x0, rd_amhandler = 0, 
  rd_indexcxt = 0x0, rd_amroutine = 0x0, rd_opfamily = 0x0, rd_opcintype = 0x0, rd_support = 0x0, rd_supportinfo = 0x0, 
  rd_indoption = 0x0, rd_indexprs = 0x0, rd_indpred = 0x0, rd_exclops = 0x0, rd_exclprocs = 0x0, rd_exclstrats = 0x0, 
  rd_amcache = 0x0, rd_indcollation = 0x0, rd_fdwroutine = 0x0, rd_toastoid = 0, pgstat_info = 0x1d5a030}
(gdb)

vacuum參數(shù)

(gdb) p *params
$2 = {freeze_min_age = -1, freeze_table_age = -1, multixact_freeze_min_age = -1, multixact_freeze_table_age = -1, 
  is_wraparound = false, log_min_duration = -1}
(gdb)

buffer訪問策略對象

(gdb) p *bstrategy
$3 = {btype = BAS_VACUUM, ring_size = 32, current = 0, current_was_in_ring = false, buffers = 0x1da9718}
(gdb) 
(gdb) p *bstrategy->buffers
$4 = 0
(gdb)

1.初始化相關(guān)變量,如本地變量/日志記錄級別/訪問策略等

$4 = 0
(gdb) n
215     if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
(gdb) 
221     if (options & VACOPT_VERBOSE)
(gdb) 
224         elevel = DEBUG2;
(gdb) 
226     pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM,
(gdb) 
229     vac_strategy = bstrategy;
(gdb)

2.調(diào)用vacuum_set_xid_limits計算最老的xmin和凍結(jié)截止點(diǎn)
返回值均為默認(rèn)值,其中OldestXmin是當(dāng)前最小的活動事務(wù)ID

231     vacuum_set_xid_limits(onerel,
(gdb) 
245     aggressive = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,
(gdb) p OldestXmin
$5 = 307590
(gdb) p FreezeLimit
$6 = 4245274886
(gdb) p xidFullScanLimit
$10 = 4145274886
(gdb) p MultiXactCutoff
$8 = 4289967297
(gdb) p mxactFullScanLimit
$9 = 4144967297
(gdb)

3.判斷是否執(zhí)行全表(不跳過pages)掃描,標(biāo)記變量為aggressive,值為F

(gdb) n
247     aggressive |= MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid,
(gdb) p aggressive
$11 = false
(gdb) n
249     if (options & VACOPT_DISABLE_PAGE_SKIPPING)
(gdb) p onerel->rd_rel->relfrozenxid
$12 = 144983
(gdb) p xidFullScanLimit
$13 = 4145274886
(gdb)

4.初始化統(tǒng)計信息結(jié)構(gòu)體vacrelstats

(gdb) n
252     vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
(gdb) 
254     vacrelstats->old_rel_pages = onerel->rd_rel->relpages;
(gdb) 
255     vacrelstats->old_live_tuples = onerel->rd_rel->reltuples;
(gdb) 
256     vacrelstats->num_index_scans = 0;
(gdb) 
257     vacrelstats->pages_removed = 0;
(gdb) 
258     vacrelstats->lock_waiter_detected = false;
(gdb) 
261     vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel);
(gdb) p *vacrelstats
$14 = {hasindex = false, old_rel_pages = 75, rel_pages = 0, scanned_pages = 0, pinskipped_pages = 0, 
  frozenskipped_pages = 0, tupcount_pages = 0, old_live_tuples = 10000, new_rel_tuples = 0, new_live_tuples = 0, 
  new_dead_tuples = 0, pages_removed = 0, tuples_deleted = 0, nonempty_pages = 0, num_dead_tuples = 0, max_dead_tuples = 0, 
  dead_tuples = 0x0, num_index_scans = 0, latestRemovedXid = 0, lock_waiter_detected = false}
(gdb)

5.打開索引,執(zhí)行函數(shù)lazy_scan_heap進(jìn)行vacuuming,關(guān)閉索引

(gdb) n
262     vacrelstats->hasindex = (nindexes > 0);
(gdb) 
265     lazy_scan_heap(onerel, options, vacrelstats, Irel, nindexes, aggressive);
(gdb) 
268     vac_close_indexes(nindexes, Irel, NoLock);
(gdb) 
(gdb) 
277     if ((vacrelstats->scanned_pages + vacrelstats->frozenskipped_pages)
(gdb) 
278         < vacrelstats->rel_pages)
(gdb) 
277     if ((vacrelstats->scanned_pages + vacrelstats->frozenskipped_pages)
(gdb) 
284         scanned_all_unfrozen = true;
(gdb) p *vacrelstats
$15 = {hasindex = true, old_rel_pages = 75, rel_pages = 75, scanned_pages = 75, pinskipped_pages = 0, 
  frozenskipped_pages = 0, tupcount_pages = 75, old_live_tuples = 10000, new_rel_tuples = 10154, new_live_tuples = 10000, 
  new_dead_tuples = 154, pages_removed = 0, tuples_deleted = 0, nonempty_pages = 75, num_dead_tuples = 0, 
  max_dead_tuples = 21825, dead_tuples = 0x1db5030, num_index_scans = 0, latestRemovedXid = 0, lock_waiter_detected = false}
(gdb) p vacrelstats->scanned_pages
$16 = 75
(gdb) p vacrelstats->frozenskipped_pages
$17 = 0
(gdb) p vacrelstats->rel_pages
$18 = 75
(gdb)

6.更新pg_class中的統(tǒng)計信息

(gdb) n
289     if (should_attempt_truncation(vacrelstats))
(gdb) 
293     pgstat_progress_update_param(PROGRESS_VACUUM_PHASE,
(gdb) 
317     new_rel_pages = vacrelstats->rel_pages;
(gdb) 
318     new_live_tuples = vacrelstats->new_live_tuples;
(gdb) 
319     if (vacrelstats->tupcount_pages == 0 && new_rel_pages > 0)
(gdb) 
325     visibilitymap_count(onerel, &new_rel_allvisible, NULL);
(gdb) 
326     if (new_rel_allvisible > new_rel_pages)
(gdb) p new_rel_allvisible
$19 = 0
(gdb) p new_rel_pages
$20 = 75
(gdb) n
329     new_frozen_xid = scanned_all_unfrozen ? FreezeLimit : InvalidTransactionId;
(gdb) 
330     new_min_multi = scanned_all_unfrozen ? MultiXactCutoff : InvalidMultiXactId;
(gdb) 
336                         vacrelstats->hasindex,
(gdb) 
332     vac_update_relstats(onerel,
(gdb) p new_frozen_xid
$21 = 4245274886
(gdb) p new_min_multi
$22 = 4289967297
(gdb)

7.收尾工作

(gdb) n
345                          vacrelstats->new_dead_tuples);
(gdb) 
342     pgstat_report_vacuum(RelationGetRelid(onerel),
(gdb) 
343                          onerel->rd_rel->relisshared,
(gdb) 
342     pgstat_report_vacuum(RelationGetRelid(onerel),
(gdb) 
346     pgstat_progress_end_command();
(gdb) 
349     if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
(gdb)

完成調(diào)用

411 }
(gdb) 
vacuum_rel (relid=50820, relation=0x1cdb8d0, options=1, params=0x7ffe010d5b70) at vacuum.c:1560
1560        AtEOXact_GUC(false, save_nestlevel);
(gdb)

到此,關(guān)于“PostgreSQL的vacuum過程中heap_vacuum_rel函數(shù)分析”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

網(wǎng)站名稱:PostgreSQL的vacuum過程中heap_vacuum_rel函數(shù)分析
URL網(wǎng)址:http://www.muchs.cn/article6/ghgiig.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊、ChatGPT營銷型網(wǎng)站建設(shè)、移動網(wǎng)站建設(shè)、全網(wǎng)營銷推廣、網(wǎng)站排名

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司