真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

PostgreSQL中vacuum過程分析

本篇內(nèi)容主要講解“PostgreSQL中vacuum過程分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“PostgreSQL中vacuum過程分析”吧!

目前成都創(chuàng)新互聯(lián)已為成百上千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)站空間、網(wǎng)站改版維護(hù)、企業(yè)網(wǎng)站設(shè)計(jì)、科爾沁網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。

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

宏定義
Vacuum和Analyze命令選項(xiàng)

/* ----------------------
 *      Vacuum and Analyze Statements
 *      Vacuum和Analyze命令選項(xiàng)
 * 
 * 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在選項(xiàng)中設(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
存儲(chǔ)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í)的定制化參數(shù)
 */
typedef struct VacuumParams
{
    //最小freeze age,-1表示使用默認(rèn)
    int         freeze_min_age; /* min freeze age, -1 to use default */
    //掃描整個(gè)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)系字段可以為空;如果不是,則僅用于報(bào)告未能打開/鎖定關(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;

二、源碼解讀

vacuum是VACUUM/ANALYZE命令的內(nèi)部處理入口.
邏輯比較簡單:
1.配置vacuum處理的相關(guān)參數(shù),如命令類型等
2.執(zhí)行相關(guān)檢查
3.構(gòu)造vacuum處理上下文
4.構(gòu)造vacuum需處理的relation鏈表
5.循環(huán)遍歷relation鏈表
 5.1 獲取relation
 5.2 執(zhí)行vacuum_rel
6.收尾工作

/*
 * Internal entry point for VACUUM and ANALYZE commands.
 * VACUUM/ANALYZE命令的內(nèi)部處理入口
 *
 * options is a bitmask of VacuumOption flags, indicating what to do.
 * options是VacuumOption選項(xiàng)標(biāo)記位,指示應(yīng)該做什么.
 *
 * relations, if not NIL, is a list of VacuumRelation to process; otherwise,
 * we process all relevant tables in the database.  For each VacuumRelation,
 * if a valid OID is supplied, the table with that OID is what to process;
 * otherwise, the VacuumRelation's RangeVar indicates what to process.
 * relations,如果不是空指針NIL,那么存儲(chǔ)了待處理的VacuumRelation結(jié)構(gòu)體鏈表.
 * 如為NIL,將處理數(shù)據(jù)庫中的所有相關(guān)數(shù)據(jù)表.
 * 對(duì)每一個(gè)VacuumRelation,如提供了有效OID,該OID對(duì)應(yīng)table就會(huì)被處理,
 *   否則,VacuumRelation的RangeVar指示了如何處理.
 *
 * params contains a set of parameters that can be used to customize the
 * behavior.
 * params是客戶端定制的參數(shù)集合.
 *
 * bstrategy is normally given as NULL, but in autovacuum it can be passed
 * in to use the same buffer strategy object across multiple vacuum() calls.
 * bstrategy通常是NULL,但在autovacuum中,
 *   該參數(shù)可用于指示在多個(gè)vacuum()調(diào)用中使用同樣的緩存strategy object
 *
 * isTopLevel should be passed down from ProcessUtility.
 * isTopLevel通過ProcessUtility向下傳遞
 *
 * It is the caller's responsibility that all parameters are allocated in a
 * memory context that will not disappear at transaction commit.
 * 調(diào)用者應(yīng)確保所有的參數(shù)在同一個(gè)內(nèi)存上下文分配內(nèi)存,而不會(huì)在事務(wù)commit時(shí)突然消失.
 */
void
vacuum(int options, List *relations, VacuumParams *params,
       BufferAccessStrategy bstrategy, bool isTopLevel)
{
    static bool in_vacuum = false;//是否在vacuum
    const char *stmttype;//語句類型,vacuum?analyze?
    volatile bool in_outer_xact,
                use_own_xacts;
    Assert(params != NULL);
    stmttype = (options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
    /*
     * We cannot run VACUUM inside a user transaction block; if we were inside
     * a transaction, then our commit- and start-transaction-command calls
     * would not have the intended effect!  There are numerous other subtle
     * dependencies on this, too.
     * 不能在用戶事務(wù)塊中運(yùn)行VACUUM,如果我們在事務(wù)塊中,
     *   那么處理過程中的commit-和start-transaction-command調(diào)用不會(huì)有正確的效果.
     * 而且還有許多其他微妙的依賴關(guān)系。
     *
     * ANALYZE (without VACUUM) can run either way.
     * ANALYZE(不帶VACUUM)則沒有此問題.
     */
    if (options & VACOPT_VACUUM)
    {
        PreventInTransactionBlock(isTopLevel, stmttype);
        in_outer_xact = false;
    }
    else
        in_outer_xact = IsInTransactionBlock(isTopLevel);
    /*
     * Due to static variables vac_context, anl_context and vac_strategy,
     * vacuum() is not reentrant.  This matters when VACUUM FULL or ANALYZE
     * calls a hostile index expression that itself calls ANALYZE.
     * 鑒于vac_context, anl_context and vac_strategy這是變量都是靜態(tài)變量,
     *   因此vacuum()函數(shù)是不能重入的(狀態(tài)已出現(xiàn)變化).
     * 在VACUUM FULL或者ANALYZE調(diào)用了hostile index expression,
     *   而此邏輯又調(diào)用了ANALYZE時(shí)會(huì)出現(xiàn)此情況,務(wù)必注意.
     */
    if (in_vacuum)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("%s cannot be executed from VACUUM or ANALYZE",
                        stmttype)));
    /*
     * Sanity check DISABLE_PAGE_SKIPPING option.
     * 檢查
     */
    if ((options & VACOPT_FULL) != 0 &&
        (options & VACOPT_DISABLE_PAGE_SKIPPING) != 0)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL")));
    /*
     * Send info about dead objects to the statistics collector, unless we are
     * in autovacuum --- autovacuum.c does this for itself.
     * 發(fā)送dead objects的統(tǒng)計(jì)信息給收集器,除非我們在autovacuum中
     * -- autovacuum.c會(huì)自己做這個(gè)事情.
     */
    if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
        pgstat_vacuum_stat();
    /*
     * Create special memory context for cross-transaction storage.
     * 跨事務(wù)存儲(chǔ),需要?jiǎng)?chuàng)建特別的內(nèi)存上下文.
     *
     * Since it is a child of PortalContext, it will go away eventually even
     * if we suffer an error; there's no need for special abort cleanup logic.
     * 因?yàn)檫@是PortalContext的子對(duì)象,即使我們犯了錯(cuò)誤,它最終也會(huì)消失;不需要特殊的中止清理邏輯。
     */
    vac_context = AllocSetContextCreate(PortalContext,
                                        "Vacuum",
                                        ALLOCSET_DEFAULT_SIZES);
    /*
     * If caller didn't give us a buffer strategy object, make one in the
     * cross-transaction memory context.
     * 如果調(diào)用者沒有提供buffer strategy object,
     *   在跨事務(wù)的內(nèi)存上下文中創(chuàng)建一個(gè).
     */
    if (bstrategy == NULL)
    {
        MemoryContext old_context = MemoryContextSwitchTo(vac_context);
        bstrategy = GetAccessStrategy(BAS_VACUUM);
        MemoryContextSwitchTo(old_context);
    }
    vac_strategy = bstrategy;
    /*
     * Build list of relation(s) to process, putting any new data in
     * vac_context for safekeeping.
     * 構(gòu)建要處理的關(guān)系列表,將所有新數(shù)據(jù)放入vac_context中以進(jìn)行安全(位于vacuum上下文中)保存。
     */
    if (relations != NIL)
    {
        List       *newrels = NIL;
        ListCell   *lc;
        foreach(lc, relations)
        {
            VacuumRelation *vrel = lfirst_node(VacuumRelation, lc);
            List       *sublist;
            MemoryContext old_context;
            sublist = expand_vacuum_rel(vrel, options);
            old_context = MemoryContextSwitchTo(vac_context);
            newrels = list_concat(newrels, sublist);
            MemoryContextSwitchTo(old_context);
        }
        relations = newrels;
    }
    else
        relations = get_all_vacuum_rels(options);
    /*
     * Decide whether we need to start/commit our own transactions.
     * 確定是否需要start/commit自己的事務(wù)
     *
     * For VACUUM (with or without ANALYZE): always do so, so that we can
     * release locks as soon as possible.  (We could possibly use the outer
     * transaction for a one-table VACUUM, but handling TOAST tables would be
     * problematic.)
     * 對(duì)于VACUUM(包含或不包含ANALYZE):通常需要這樣處理,以便我們可以盡可能快的釋放鎖.
     * (對(duì)于一張表的VACUUM,我們可能使用外層事務(wù),但處理TOAST表是會(huì)有問題)
     *
     * For ANALYZE (no VACUUM): if inside a transaction block, we cannot
     * start/commit our own transactions.  Also, there's no need to do so if
     * only processing one relation.  For multiple relations when not within a
     * transaction block, and also in an autovacuum worker, use own
     * transactions so we can release locks sooner.
     * 對(duì)于ANALYZE(沒有VACUUM選項(xiàng)):如果在事務(wù)塊中,我們不能start/commit自己的事務(wù).
     * 同時(shí),如果只需要處理一個(gè)relation,則不需要這樣處理.
     * 對(duì)于不在一個(gè)事務(wù)塊中的多個(gè)relations/在autovacuum worker中,
     *   使用自己的事務(wù)以便更快的釋放鎖.
     */
    if (options & VACOPT_VACUUM)
        use_own_xacts = true;
    else
    {
        Assert(options & VACOPT_ANALYZE);
        if (IsAutoVacuumWorkerProcess())
            use_own_xacts = true;
        else if (in_outer_xact)
            use_own_xacts = false;
        else if (list_length(relations) > 1)
            use_own_xacts = true;
        else
            use_own_xacts = false;
    }
    /*
     * vacuum_rel expects to be entered with no transaction active; it will
     * start and commit its own transaction.  But we are called by an SQL
     * command, and so we are executing inside a transaction already. We
     * commit the transaction started in PostgresMain() here, and start
     * another one before exiting to match the commit waiting for us back in
     * PostgresMain().
     * 在進(jìn)入vacuum_rel前,不希望存在事務(wù)活動(dòng).該函數(shù)會(huì)啟動(dòng)和提交自己的事務(wù).
     * 但由于我們是通過SQL命令調(diào)用的,因此我們已處于事務(wù)中執(zhí)行.
     * 在這里我們提交在PostgresMain()中啟動(dòng)的事務(wù),
     * 并在退出之前啟動(dòng)另一個(gè),以匹配在PostgresMain()中等待我們的提交。
     */
    if (use_own_xacts)
    {
        Assert(!in_outer_xact);
        /* ActiveSnapshot is not set by autovacuum */
        //autovacuum不會(huì)設(shè)置ActiveSnapshot
        if (ActiveSnapshotSet())
            PopActiveSnapshot();
        /* matches the StartTransaction in PostgresMain() */
        //匹配PostgresMain()中的StartTransaction
        CommitTransactionCommand();
    }
    /* Turn vacuum cost accounting on or off, and set/clear in_vacuum */
    //設(shè)置vacuum成本計(jì)數(shù)on/off,并set/clear in_vacuum參數(shù)
    PG_TRY();
    {
        ListCell   *cur;
        in_vacuum = true;
        VacuumCostActive = (VacuumCostDelay > 0);
        VacuumCostBalance = 0;
        VacuumPageHit = 0;
        VacuumPageMiss = 0;
        VacuumPageDirty = 0;
        /*
         * Loop to process each selected relation.
         * 循環(huán)處理每一個(gè)已選中的relation.
         */
        foreach(cur, relations)
        {
            VacuumRelation *vrel = lfirst_node(VacuumRelation, cur);
            if (options & VACOPT_VACUUM)
            {
                //執(zhí)行vacuum處理
                if (!vacuum_rel(vrel->oid, vrel->relation, options, params))
                    continue;
            }
            if (options & VACOPT_ANALYZE)
            {
                /*
                 * If using separate xacts, start one for analyze. Otherwise,
                 * we can use the outer transaction.
                 * 如果使用獨(dú)立的xacts,為analyze啟動(dòng)一個(gè)何事務(wù).
                 * 否則,我們可以使用外層事務(wù).
                 */
                if (use_own_xacts)
                {
                    //使用自己的事務(wù)
                    StartTransactionCommand();
                    /* functions in indexes may want a snapshot set */
                    //快照壓棧
                    PushActiveSnapshot(GetTransactionSnapshot());
                }
                //分析relation
                analyze_rel(vrel->oid, vrel->relation, options, params,
                            vrel->va_cols, in_outer_xact, vac_strategy);
                if (use_own_xacts)
                {
                    //使用自己的事務(wù),出棧
                    PopActiveSnapshot();
                    //提交事務(wù)
                    CommitTransactionCommand();
                }
            }
        }
    }
    PG_CATCH();
    {
        in_vacuum = false;
        VacuumCostActive = false;
        PG_RE_THROW();
    }
    PG_END_TRY();
    in_vacuum = false;
    VacuumCostActive = false;
    /*
     * Finish up processing.
     * 完成處理過程
     */
    if (use_own_xacts)
    {
        /* here, we are not in a transaction */
        //在這里,沒有處于事務(wù)中
        /*
         * This matches the CommitTransaction waiting for us in
         * PostgresMain().
         * 匹配在PostgresMain()函數(shù)中等待我們的CommitTransaction.
         */
        StartTransactionCommand();
    }
    if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
    {
        /*
         * Update pg_database.datfrozenxid, and truncate pg_xact if possible.
         * (autovacuum.c does this for itself.)
         * 更新pg_database.datfrozenxid,如可能截?cái)鄍g_xact.
         * (autovacuum.c不會(huì)處理這事情)
         */
        vac_update_datfrozenxid();
    }
    /*
     * Clean up working storage --- note we must do this after
     * StartTransactionCommand, else we might be trying to delete the active
     * context!
     * 清除工作存儲(chǔ) --- 注意必須在StartTransactionCommand命令后執(zhí)行清除過程,
     *   否則我們可能會(huì)嘗試刪除活動(dòng)的上下文.
     */
    MemoryContextDelete(vac_context);
    vac_context = NULL;
}

三、跟蹤分析

測試腳本

17:19:28 (xdb@[local]:5432)testdb=# vacuum t1;

啟動(dòng)gdb,設(shè)置斷點(diǎn)

(gdb) b vacuum
Breakpoint 1 at 0x6b9b8c: file vacuum.c, line 175.
(gdb) c
Continuing.
Breakpoint 1, vacuum (options=1, relations=0x2294988, params=0x7fff403d8880, bstrategy=0x0, isTopLevel=true) at vacuum.c:175
175     Assert(params != NULL);
(gdb)

輸入?yún)?shù)
options=1 —> VACOPT_VACUUM
relations=0x2294988,relation鏈表,里面只有一個(gè)item,即t1
params=0x7fff403d8880,默認(rèn)參數(shù)
bstrategy=NULL,
isTopLevel=T,為頂層事務(wù)

(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)

變量賦值并執(zhí)行相關(guān)判斷

(gdb) n
177     stmttype = (options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
(gdb) 
187     if (options & VACOPT_VACUUM)
(gdb) 
189         PreventInTransactionBlock(isTopLevel, stmttype);
(gdb) 
190         in_outer_xact = false;
(gdb) 
200     if (in_vacuum)
(gdb) 
209     if ((options & VACOPT_FULL) != 0 &&
(gdb)

統(tǒng)計(jì)信息

219     if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
(gdb) 
220         pgstat_vacuum_stat();
(gdb)

創(chuàng)建并設(shè)置內(nèi)存上下文

(gdb) n
228     vac_context = AllocSetContextCreate(PortalContext,
(gdb) 
236     if (bstrategy == NULL)
(gdb) 
238         MemoryContext old_context = MemoryContextSwitchTo(vac_context);
(gdb) 
240         bstrategy = GetAccessStrategy(BAS_VACUUM);
(gdb) 
241         MemoryContextSwitchTo(old_context);
(gdb) 
243     vac_strategy = bstrategy;
(gdb) 
249     if (relations != NIL)
(gdb)

構(gòu)造VacuumRelation鏈表

(gdb) 
251         List       *newrels = NIL;
(gdb) 
254         foreach(lc, relations)
(gdb) 
256             VacuumRelation *vrel = lfirst_node(VacuumRelation, lc);
(gdb) 
260             sublist = expand_vacuum_rel(vrel);
(gdb) p *vrel
$3 = {type = T_VacuumRelation, relation = 0x22948d0, oid = 0, va_cols = 0x0}
(gdb) p *vrel->relation
$4 = {type = T_RangeVar, catalogname = 0x0, schemaname = 0x0, relname = 0x22948b0 "t1", inh = true, 
  relpersistence = 112 'p', alias = 0x0, location = 7}
(gdb) 
(gdb) n
261             old_context = MemoryContextSwitchTo(vac_context);
(gdb) 
262             newrels = list_concat(newrels, sublist);
(gdb) 
263             MemoryContextSwitchTo(old_context);
(gdb) 
254         foreach(lc, relations)
(gdb) 
265         relations = newrels;
(gdb)

使用自主事務(wù)

284     if (options & VACOPT_VACUUM)
(gdb) 
285         use_own_xacts = true;
(gdb) 
307     if (use_own_xacts)
(gdb) 
307     if (use_own_xacts)
(gdb) 
309         Assert(!in_outer_xact);
(gdb) 
312         if (ActiveSnapshotSet())
(gdb) 
313             PopActiveSnapshot();
(gdb) 
316         CommitTransactionCommand();
(gdb) 
320     PG_TRY();
(gdb)

開始執(zhí)行,設(shè)置vacuum成本計(jì)數(shù)on/off,并set/clear in_vacuum參數(shù)

(gdb) 
324         in_vacuum = true;
(gdb) 
325         VacuumCostActive = (VacuumCostDelay > 0);
(gdb) 
326         VacuumCostBalance = 0;
(gdb) 
327         VacuumPageHit = 0;
(gdb) 
328         VacuumPageMiss = 0;
(gdb) 
329         VacuumPageDirty = 0;
(gdb)

循環(huán)relation,調(diào)用vacuum_rel

334         foreach(cur, relations)
(gdb) 
336             VacuumRelation *vrel = lfirst_node(VacuumRelation, cur);
(gdb) 
338             if (options & VACOPT_VACUUM)
(gdb) 
340                 if (!vacuum_rel(vrel->oid, vrel->relation, options, params))
(gdb) 
344             if (options & VACOPT_ANALYZE)
(gdb) 
334         foreach(cur, relations)
(gdb) 
374     PG_END_TRY();
(gdb)

執(zhí)行收尾工作

(gdb) 
376     in_vacuum = false;
(gdb) 
377     VacuumCostActive = false;
(gdb) 
382     if (use_own_xacts)
(gdb) 
390         StartTransactionCommand();
(gdb) 
393     if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
(gdb) 
399         vac_update_datfrozenxid();
(gdb) 
407     MemoryContextDelete(vac_context);
(gdb) 
408     vac_context = NULL;
(gdb)

完成調(diào)用

409 }
(gdb) 
ExecVacuum (vacstmt=0x22949c0, isTopLevel=true) at vacuum.c:142
142 }
(gdb)

到此,相信大家對(duì)“PostgreSQL中vacuum過程分析”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!


當(dāng)前名稱:PostgreSQL中vacuum過程分析
網(wǎng)站鏈接:http://weahome.cn/article/jhdcjo.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部