• 【 OpenGauss源码学习 —— 列存储(autoanalyze)】


    声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。
    本文主要参考了 OpenGauss1.1.0 的开源代码和《OpenGauss数据库源码解析》一书以及OpenGauss社区学习文档和一些学习资料

    概述

      列存储autoanalyze)是一种数据库自动维护统计信息的功能,它定期分析表中的数据分布和变化情况,以优化查询性能。自动分析通过检查表的内容,确定最佳查询计划,从而帮助数据库优化查询操作,提高查询性能,特别是在大型数据库中。这个过程会定期运行,通常由数据库自动调度,以确保统计信息的准确性和最新性,以便数据库查询优化器可以更好地选择执行计划。这有助于提高数据库的性能和响应速度减少了手动维护统计信息的工作负担

    relation_support_autoavac 函数

      relation_support_autoavac 函数的作用是确定一个表是否支持自动执行 ANALYZE 或 VACUUM 操作,并返回相关的标志。它根据表的关系选项属性信息,检查表是否为内部表行存表列存表,并根据配置参数决定是否支持 ANALYZEVACUUM 操作。同时,该函数也确定表是否为内部表,以及是否需要跳过统计信息表TOAST 表和配置不支持 ANALYZEVACUUM 操作的表。最终,函数设置相应的标志,指示是否支持 ANALYZEVACUUM 操作,以及表是否为内部表
      relation_support_autoavac 函数源码如下:(路径:src/gausskernel/process/postmaster/autovacuum.cpp

    /*
     * 检查表是否支持自动执行 ANALYZE 或 VACUUM 操作
     */
    void relation_support_autoavac(HeapTuple tuple, bool* enable_analyze, bool* enable_vacuum, bool* is_internal_relation)
    {
        bytea* relopts = NULL; // 存储表的关系选项
        Form_pg_class classForm = (Form_pg_class)GETSTRUCT(tuple); // 获取表的属性数据结构
    
        Assert(PointerIsValid(enable_analyze));
        Assert(PointerIsValid(enable_vacuum));
    
        *enable_analyze = false; // 默认情况下不支持 ANALYZE 操作
        *enable_vacuum = false; // 默认情况下不支持 VACUUM 操作
        *is_internal_relation = false; // 默认情况下不是内部表
    
        /* 跳过所有自动 VACUUM 操作 */
        if (IS_PGXC_COORDINATOR && !u_sess->attr.attr_storage.autovacuum_start_daemon)
            return;
    
        /*
         * 1. 不对内部表执行 ANALYZE,因为我们通常不会直接选择它们
         * 2. 对于行存表,只执行 ANALYZE 操作,因为执行 HDFS 表的 ANALYZE 操作效率太低
         */
        relopts = extractRelOptions(tuple, GetDefaultPgClassDesc(), InvalidOid); // 提取表的关系选项
        if (StdRelOptGetInternalMask(relopts)) {
            /* 什么都不做,但将 is_internal_relation 设置为 true */
            *is_internal_relation = true;
    
            /* 如果表是 MLOG 或 MATMAP,则设置相应的标志 */
            if (StdRelOptIsRowStore(relopts) && (ISMATMAP(classForm->relname.data) || ISMLOG(classForm->relname.data))) {
                *enable_analyze = true;
                *enable_vacuum = true;
            }
        } else if (StdRelOptIsColStore(relopts)) {
            *enable_analyze = true; // 列存表支持 ANALYZE 操作
        } else if (StdRelOptIsRowStore(relopts)) {
            *enable_analyze = true; // 行存表支持 ANALYZE 操作
            *enable_vacuum = true; // 行存表支持 VACUUM 操作
        }
    
        // 如果是统计信息表、TOAST 表或配置不支持 ANALYZE 或 VACUUM 操作,则设置相应的标志
        if (StatisticRelationId == HeapTupleGetOid(tuple) || RELKIND_TOASTVALUE == classForm->relkind || !DO_ANALYZE) {
            *enable_analyze = false;
        }
    
        if (!DO_VACUUM) {
            *enable_vacuum = false;
        }
    
        if (relopts != NULL)
            pfree_ext(relopts);
    
        /*
         * 1. 临时表或非持久表中的数据是短暂的,因此不对它们执行 ANALYZE 操作
         * 2. 外部表没有统计信息,因此仅支持普通表
         */
        if (RELPERSISTENCE_PERMANENT != classForm->relpersistence || RELKIND_RELATION != classForm->relkind) {
            *enable_analyze = false;
            *is_internal_relation = false;
        }
        if (RELKIND_RELATION != classForm->relkind) {
            *enable_vacuum = false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

      以下是函数 relation_support_autoavac 的主要功能作用

    1. 首先,它接收一个 HeapTuple,该元组代表了一个,并且通过传递指针参数 enable_analyzeenable_vacuum返回表是否支持执行 ANALYZEVACUUM 操作的信息。还有一个 is_internal_relation 参数,用于指示表是否是内部表
    2. 函数开始时将 enable_analyzeenable_vacuumis_internal_relation 都设置为 false表示默认情况下不支持执行 ANALYZEVACUUM 操作,且不是内部表
    3. 接下来,函数检查是否在分布式架构下运行(IS_PGXC_COORDINATOR)并且是否启用了自动化 VACUUM 进程autovacuum_start_daemon 配置)。如果不是,则不执行任何自动 ANALYZEVACUUM 操作,直接返回。
    4. 函数接着提取表的关系选项(relopts),并根据这些选项来判断表是否支持自动 ANALYZEVACUUM 操作
    • 如果表被标记为内部表(StdRelOptGetInternalMask(relopts)),则不执行自动 ANALYZEVACUUM,但将 is_internal_relation 设置为 true
    • 对于行存表StdRelOptIsRowStore(relopts),设置 enable_analyzeenable_vacuumtrue。如果表的名称标识它是 MLOGMATMAP 类型的,则同样设置 enable_analyzeenable_vacuumtrue。如果是列存表(StdRelOptIsColStore(relopts)),则只设置 enable_analyzetrue
    1. 接着,根据表的类型和一些配置参数进行进一步检查和设置。如果表是统计信息表TOAST 表或配置不支持 ANALYZEVACUUM 操作,则将相应的标志设置为 false
    2. 最后,如果表的持久性不是永久性RELPERSISTENCE_PERMANENT)或者表的类型不是关系表(RELKIND_RELATION),则将 enable_analyzeis_internal_relation 设置为 false。如果表的类型不是关系表,将 enable_vacuum 设置为 false

    relation_needs_vacanalyze 函数

      relation_needs_vacanalyze 函数的作用是确定一个表是否需要执行自动化的 VACUUM 或 ANALYZE 操作,并返回相应的决策。它还会判断是否需要强制执行 VACUUM 操作,以便回收CLOG中的旧元组

    relation_needs_vacanalyze 函数接受以下参数:

    • Oid relid表的对象标识符(OID)。
    • AutoVacOpts* relopts自动化清理选项,用于配置自动 VACUUMANALYZE 行为。
    • Form_pg_class classForm表的 pg_class 形式,包含有关表的元数据信息。
    • HeapTuple tuple包含有关表的额外信息的元组。
    • PgStat_StatTabEntry* tabentry关于表的性能统计信息表项,可以为 NULL
    • bool allowAnalyze指示是否允许执行 ANALYZE 操作的布尔值。
    • bool allowVacuum指示是否允许执行 VACUUM 操作的布尔值。
    • bool is_recheck标志是否正在重新检查表的状态的布尔值。
    • bool* dovacuum指针,用于返回是否需要执行 VACUUM 操作的布尔值。
    • bool* doanalyze指针,用于返回是否需要执行 ANALYZE 操作的布尔值。
    • bool* need_freeze指针,用于返回是否需要强制执行 VACUUM 操作以回收 CLOG 中的旧元组的布尔值。

      relation_needs_vacanalyze 函数源码如下:(路径:src/gausskernel/process/postmaster/autovacuum.cpp

    /*
     * relation_needs_vacanalyze
     *
     * 检查表是否需要执行自动清理(vacuum)或自动分析(analyze);
     * 分别将结果存储在 "dovacuum" 和 "doanalyze" 中。
     * 同时返回是否由于需要回收CLOG(事务变更日志)中的旧元组而强制执行清理的信息。
     *
     * relopts 是指向 AutoVacOpts 选项的指针(对于普通表自身,或者对于 TOAST 表自身或其父表),
     * 如果不存在选项则为 NULL;tabentry 是 pgstats(PostgreSQL统计信息)中的条目,可以为空。
     */
    static void relation_needs_vacanalyze(Oid relid, AutoVacOpts* relopts, Form_pg_class classForm, HeapTuple tuple,
        PgStat_StatTabEntry* tabentry, bool allowAnalyze, bool allowVacuum, bool is_recheck,
        /* 下面是输出参数 */
        bool* dovacuum, bool* doanalyze, bool* need_freeze)
    {
        PgStat_StatTabKey tablekey;
        avw_info* avwentry = NULL;
        bool found = false;
        bool force_vacuum = false;
        bool av_enabled = false;
        /* pg_class.reltuples */
        float4 reltuples;
    
        /* 从 reloptions 或 GUC 变量中获取的常数值 */
        int vac_base_thresh = 0;
        int anl_base_thresh = 0;
        float4 vac_scale_factor = 0.0;
        float4 anl_scale_factor = 0.0;
    
        /* 根据上述常数值计算的阈值 */
        float4 vacthresh;
        float4 anlthresh;
    
        /* 此时的清理(或分析)元组数量 */
        int64 vactuples = 0;
        int64 anltuples = 0;
    
        /* 冻结参数 */
        int64 freeze_max_age = 0;
        TransactionId xidForceLimit = InvalidTransactionId;
    
        AssertArg(classForm != NULL);
        AssertArg(OidIsValid(relid));
    
        // 确定清理参数,包括清理阈值、分析阈值、强制清理的条件等
        determine_vacuum_params(vac_scale_factor, vac_base_thresh, anl_scale_factor, anl_base_thresh, freeze_max_age,
            av_enabled, xidForceLimit, relopts);
    
        bool isNull = false;
        TransactionId relfrozenxid = InvalidTransactionId;
        Relation rel = heap_open(RelationRelationId, AccessShareLock);
    
        // 从元组中获取 relfrozenxid64,如果为空则获取 relfrozenxid
        Datum xid64datum = heap_getattr(tuple, Anum_pg_class_relfrozenxid64, RelationGetDescr(rel), &isNull);
        heap_close(rel, AccessShareLock);
    
        if (isNull) {
            relfrozenxid = classForm->relfrozenxid;
    
            if (TransactionIdPrecedes(t_thrd.xact_cxt.ShmemVariableCache->nextXid, relfrozenxid) ||
                !TransactionIdIsNormal(relfrozenxid)) {
                relfrozenxid = FirstNormalTransactionId;
            }
        } else {
            relfrozenxid = DatumGetTransactionId(xid64datum);
        }
    
        // 检查是否需要强制执行清理
        force_vacuum = (TransactionIdIsNormal(relfrozenxid) && TransactionIdPrecedes(relfrozenxid, xidForceLimit));
        *need_freeze = force_vacuum;
        AUTOVAC_LOG(DEBUG2, "vac \"%s\": need freeze is %s", NameStr(classForm->relname), force_vacuum ? "true" : "false");
    
        // 如果用户在 pg_class.reloptions 中禁用了清理,或者禁用了 autovacuum_start_daemon,则跳过清理和分析
        if (!force_vacuum && (!av_enabled || !u_sess->attr.attr_storage.autovacuum_start_daemon)) {
            *doanalyze = false;
            *dovacuum = false;
            return;
        }
    
        // 如果 pgStatAutoVacInfo 不为空,获取统计信息信息
        if (NULL != t_thrd.autovacuum_cxt.pgStatAutoVacInfo) {
            tablekey.statFlag = InvalidOid;
            tablekey.tableid = relid;
            avwentry =
                (avw_info*)hash_search(t_thrd.autovacuum_cxt.pgStatAutoVacInfo, (void*)(&tablekey), HASH_FIND, &found);
        }
    
        reltuples = classForm->reltuples;
        vacthresh = (float4)vac_base_thresh + vac_scale_factor * reltuples;
        anlthresh = (float4)anl_base_thresh + anl_scale_factor * reltuples;
    
        // 根据统计信息和阈值确定是否需要执行清理和分析
        if ((avwentry == NULL) && (tabentry == NULL)) {
            *dovacuum = force_vacuum;
            *doanalyze = false;
        } else {
            if (tabentry && (tabentry->changes_since_analyze || tabentry->n_dead_tuples)) {
                anltuples = tabentry->changes_since_analyze;
                vactuples = tabentry->n_dead_tuples;
                AUTOVAC_LOG(DEBUG2, "fetch local stat info: vac \"%s\" changes_since_analyze = %ld  n_dead_tuples = %ld ",
                    NameStr(classForm->relname), tabentry->changes_since_analyze, tabentry->n_dead_tuples);
            }
    
            if (avwentry && (avwentry->changes_since_analyze || avwentry->n_dead_tuples)) {
                anltuples = avwentry->changes_since_analyze;
                vactuples = avwentry->n_dead_tuples;
                AUTOVAC_LOG(DEBUG2, "fetch global stat info: vac \"%s\" changes_since_analyze = %ld  n_dead_tuples = %ld ",
                    NameStr(classForm->relname), avwentry->changes_since_analyze, avwentry->n_dead_tuples);
            }
    
            // 确定表是否需要清理
            *dovacuum = force_vacuum;
            *doanalyze = false;
    
            if (false == *dovacuum && allowVacuum)
                *dovacuum = ((float4)vactuples > vacthresh);
    
            // 确定表是否需要分析
            if (allowAnalyze)
                *doanalyze = ((float4)anltuples > anlthresh);
        }
    
        // 输出调试信息,包括是否需要清理、是否需要分析等
        if (*dovacuum || *doanalyze) {
            AUTOVAC_LOG(DEBUG2, "vac \"%s\": recheck = %s need_freeze = %s"
                "dovacuum = %s (dead tuples %ld vacuum threshold %.0f) "
                "doanalyze = %s (changed tuples %ld analyze threshold %.0f)",
                NameStr(classForm->relname), is_recheck ? "true" : "false", *need_freeze ? "true" : "false",
                *dovacuum ? "true" : "false", vactuples, vacthresh, *doanalyze ? "true" : "false", anltuples, anlthresh);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131

    relation_needs_vacanalyze 函数的执行过程如下:

    1. 函数接受多个输入参数,包括表的 OID自动清理分析选项表的元数据信息HeapTuple(包含表的元数据信息)、PgStat_StatTabEntry(用于表的统计信息)、是否允许分析是否允许清理是否需要重新检查等。
    2. 根据输入参数和配置参数,确定清理和分析的阈值是否需要强制执行清理(例如,由于需要回收旧事务的CLOG信息)以及是否启用了自动清理功能
    3. 获取表的冻结事务 ID,用于确定是否需要强制执行清理
    4. 检查是否禁用了自动清理功能或者是否在不允许的情况下执行自动清理,如果是,则设置不需要执行清理和分析,并返回。
    5. 如果存在表的统计信息pgStatAutoVacInfo),则获取该表的统计信息,用于进一步判断是否需要执行清理和分析
    6. 根据表的元数据信息和配置参数,计算清理和分析的阈值,这些阈值是根据表的大小配置参数动态计算的。
    7. 根据表的统计信息阈值,确定是否需要执行清理和分析。如果需要清理,则设置 dovacuumtrue;如果需要分析,则设置 doanalyzetrue
    8. 最后,输出调试信息,包括是否需要执行清理是否需要执行分析以及其他相关信息。

      其中,relation_support_autoavac 函数和 relation_needs_vacanalyze 函数之间的关系是协作的,它们一起用于确定是否需要对一个特定的表执行自动 VACUUMANALYZE 操作,并为这些操作提供必要的信息。以下是它们之间的关系:

    1. relation_support_autoavac 函数:
    • 该函数的主要作用是检查一个表是否满足自动 VACUUMANALYZE 的条件
    • 如果表满足条件,它会将 enable_analyzeenable_vacuum 设置为 true,否则设置为 false
    • 如果表是内部表临时表或不适合进行自动化清理的其他特殊类型的表,它还会将 is_internal_relation 设置为 true,以标记该表为内部表
    1. relation_needs_vacanalyze 函数:
    • 该函数用于确定是否需要对一个表执行自动 VACUUMANALYZE 操作,以及是否需要强制执行 VACUUM 操作以回收 CLOG 中的旧元组
    • 它接受 relation_support_autoavac 函数设置的 enable_analyzeenable_vacuumis_internal_relation 的值作为输入。
    • 在执行过程中,它会根据表的统计信息配置参数和其他条件来计算是否需要执行 VACUUMANALYZE
    • 最终,它会根据计算结果设置 dovacuumdoanalyze 的值,以指示是否应该执行相应的操作。

    partition_needs_vacanalyze 函数

      partition_needs_vacanalyze 函数的作用和功能是判断指定的分区表是否需要进行自动化VACUUMANALYZE 操作,根据一系列因素,包括表的统计信息配置选项冻结阈值等等,确定是否需要执行这些操作,并根据需要设置 dovacuumdoanalyze 参数,同时也判断是否需要冻结旧的元组,最后还会设置 is_internal_relation 参数以指示表是否是内部关系

    partition_needs_vacanalyze 函数接受以下参数:

    • partid分区表的对象标识符(OID)。
    • AutoVacOpts* relopts分区表的自动化清理选项,用于配置自动 VACUUMANALYZE 行为。
    • Form_pg_class partForm分区表的 pg_class 形式,包含有关表的元数据信息。
    • HeapTuple partTuple包含有关分区表的元组。
    • at_partitioned_table* ap_entry用于存储有关分区表的自动化 VACUUM 配置的数据结构。
    • PgStat_StatTabEntry* tabentry关于表的性能统计信息表项,可以为 NULL
    • bool is_recheck标志是否正在重新检查表的状态的布尔值。
    • bool* dovacuum指针,用于返回是否需要执行 VACUUM 操作的布尔值。
    • bool* doanalyze指针,用于返回是否需要执行 ANALYZE 操作的布尔值。
    • bool* need_freeze指针,用于返回是否需要冻结旧元组以回收 CLOG(事务日志)。

      partition_needs_vacanalyze 函数源码如下:(路径:src/gausskernel/process/postmaster/autovacuum.cpp

    static void partition_needs_vacanalyze(Oid partid, AutoVacOpts* relopts, Form_pg_partition partForm,
        HeapTuple partTuple, at_partitioned_table* ap_entry, PgStat_StatTabEntry* tabentry, bool is_recheck, bool* dovacuum,
        bool* doanalyze, bool* need_freeze)
    {
        PgStat_StatTabKey tablekey;
        avw_info* avwentry = NULL;
        bool found = false;
        bool av_enabled = false;
        bool force_vacuum = false;
        /* pg_partition.reltuples */
        float4 reltuples;
    
        /* 从reloptions或GUC变量中获取的常数 */
        float4 vac_scale_factor = 0.0;
        float4 anl_scale_factor = 0.0;
        int vac_base_thresh = 0;
        int anl_base_thresh = 0;
        /* 从上述常数计算的阈值 */
        float4 vacthresh;
        float4 anlthresh;
    
        /* 此时的VACUUM(或ANALYZE)元组数量 */
        int64 vactuples = 0;
        int64 anltuples = 0;
        /* 冻结参数 */
        int64 freeze_max_age = 0;
        TransactionId xidForceLimit = InvalidTransactionId;
    
        char* relname = NULL;
        char* partname = NULL;
    
        AssertArg(partForm != NULL && OidIsValid(partid));
    
        // 确定自动化VACUUM和ANALYZE操作的参数,包括比例因子、基础阈值、冻结最大年龄、是否启用自动VACUUM等
        determine_vacuum_params(vac_scale_factor, vac_base_thresh, anl_scale_factor, anl_base_thresh, freeze_max_age,
            av_enabled, xidForceLimit, relopts);
    
        // 如果最近的事务ID超过了冻结最大年龄,则强制执行VACUUM
        if (t_thrd.autovacuum_cxt.recentXid > FirstNormalTransactionId + freeze_max_age)
            xidForceLimit = t_thrd.autovacuum_cxt.recentXid - freeze_max_age;
        else
            xidForceLimit = FirstNormalTransactionId;
    
        bool isNull = false;
        TransactionId relfrozenxid = InvalidTransactionId;
        Relation rel = heap_open(PartitionRelationId, AccessShareLock);
        Datum xid64datum = heap_getattr(partTuple, Anum_pg_partition_relfrozenxid64, RelationGetDescr(rel), &isNull);
        heap_close(rel, AccessShareLock);
    
        // 如果不存在有效的relfrozenxid,则使用partForm中的默认值
        if (isNull) {
            relfrozenxid = partForm->relfrozenxid;
    
            if (TransactionIdPrecedes(t_thrd.xact_cxt.ShmemVariableCache->nextXid, relfrozenxid) ||
                !TransactionIdIsNormal(relfrozenxid)) {
                relfrozenxid = FirstNormalTransactionId;
            }
        } else {
            relfrozenxid = DatumGetTransactionId(xid64datum);
        }
    
        // 如果relfrozenxid在xidForceLimit之前,则强制执行VACUUM
        force_vacuum = (TransactionIdIsNormal(relfrozenxid) && TransactionIdPrecedes(relfrozenxid, xidForceLimit));
        *need_freeze = force_vacuum;
    
        // 如果禁用了自动化操作或不需要自动化操作,则不执行VACUUM和ANALYZE
        if (!force_vacuum && (!av_enabled || !u_sess->attr.attr_storage.autovacuum_start_daemon)) {
            *doanalyze = false;
            *dovacuum = false;
            return;
        }
    
        // 如果禁用了自动化操作或表已经被其他进程处理,则不执行VACUUM和ANALYZE
        if (!force_vacuum && (!ap_entry->at_allowvacuum || ap_entry->at_dovacuum)) {
            *doanalyze = false;
            *dovacuum = false;
            return;
        }
    
        relname = get_rel_name(partForm->parentid);
        partname = NameStr(partForm->relname);
    
        // 获取分区表的元组数量
        reltuples = partForm->reltuples;
    
        // 根据阈值计算是否需要执行VACUUM和ANALYZE操作
        anlthresh = (float4)anl_base_thresh + anl_scale_factor * reltuples;
        vacthresh = (float4)vac_base_thresh + vac_scale_factor * reltuples;
    
        if (NULL != t_thrd.autovacuum_cxt.pgStatAutoVacInfo) {
            tablekey.statFlag = partForm->parentid;
            tablekey.tableid = partid;
            avwentry =
                (avw_info*)hash_search(t_thrd.autovacuum_cxt.pgStatAutoVacInfo, (void*)(&tablekey), HASH_FIND, &found);
        }
    
        // 如果找到表的统计信息,使用统计信息来确定是否需要执行VACUUM和ANALYZE操作
        if ((avwentry == NULL) && (tabentry == NULL)) {
            *doanalyze = false;
            *dovacuum = force_vacuum;
        } else {
            if (tabentry && (tabentry->changes_since_analyze || tabentry->n_dead_tuples)) {
                vactuples = tabentry->n_dead_tuples;
                anltuples = tabentry->changes_since_analyze;
                AUTOVAC_LOG(DEBUG2, "获取本地统计信息: VAC \"%s\" 分区(\"%s\") 更改自上次ANALYZE以来的元组数 = %ld  死元组数 = %ld ",
                    relname, partname, tabentry->changes_since_analyze, tabentry->n_dead_tuples);
            }
    
            if (avwentry && (avwentry->changes_since_analyze || avwentry->n_dead_tuples)) {
                anltuples = avwentry->changes_since_analyze;
                vactuples = avwentry->n_dead_tuples;
    
                AUTOVAC_LOG(DEBUG2, "获取本地统计信息: VAC \"%s\" 分区(\"%s\") 更改自上次ANALYZE以来的元组数 = %ld  死元组数 = %ld ",
                    relname, partname, avwentry->changes_since_analyze, avwentry->n_dead_tuples);
    
                /*
                 * 刷新分区的vacthresh/anlthresh,使用n_live_tuples代替reltuples,因为我们不会对分区执行ANALYZE,
                 * 所以pg_partition中的reltuples为0,我们只使用n_live_tuples代替reltuples
                 */
                anlthresh = (float4)anl_base_thresh + anl_scale_factor * avwentry->n_live_tuples;
                vacthresh = (float4)vac_base_thresh + vac_scale_factor * avwentry->n_live_tuples;
            }
    
            /* 确定是否需要执行VACUUM操作 */
            *dovacuum = force_vacuum;
            if (false == *dovacuum)
                *dovacuum = (vactuples > vacthresh);
    
            /*
             * 确定是否需要执行ANALYZE操作。
             * 注意:我们只对分区表执行自动ANALYZE操作,不会对分区表执行ANALYZE,
             * 所以这段代码保留以备将来支持分区ANALYZE。
             */
            *doanalyze = (anltuples > anlthresh) && false;
        }
    
        // 如果不是重新检查并且需要执行VACUUM或ANALYZE操作,则记录日志
        if (!is_recheck && (*dovacuum || *doanalyze)) {
            AUTOVAC_LOG(DEBUG2, "VAC表 \"%s\" 分区(\"%s\"): 重新检查 = %s 需要冻结 = %s "
                "需要执行VACUUM = %s (死元组数 %ld VAC阈值 %.0f)",
                relname, partname, is_recheck ? "true" : "false", *need_freeze ? "true" : "false",
                *dovacuum ? "true" : "false", vactuples, vacthresh);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144

      partition_needs_vacanalyze 函数和 relation_needs_vacanalyze 函数都用于确定是否需要执行自动化 VACUUMANALYZE 操作,但它们的关注点应用场景略有不同。

    1. relation_needs_vacanalyze 函数:
    • 关注对象主要用于处理表(relation)级别的自动化 VACUUMANALYZE 决策。
    • 输入参数接受表的元数据、统计信息、配置选项等作为参数。
    • 应用场景通常用于处理普通表(non-partitioned table)的自动化 VACUUMANALYZE 决策。
    • 返回结果根据传入的参数,确定是否需要执行 VACUUMANALYZE 操作。
    1. partition_needs_vacanalyze 函数:
    • 关注对象主要用于处理分区表(partitioned table)级别的自动化 VACUUMANALYZE 决策。
    • 输入参数接受分区表的元数据、统计信息、配置选项以及与其相关的分区表信息作为参数。
    • 应用场景用于处理分区表及其分区的自动化 VACUUMANALYZE 决策。此函数通常用于决定是否需要对单个分区表执行操作。
    • 返回结果根据传入的参数,确定是否需要执行 VACUUMANALYZE 操作。

      总的来说,这两个函数都属于 PostgreSQL自动化维护功能的一部分,它们根据不同的场景需求来确定是否需要执行表级别或分区表级别的自动化 VACUUMANALYZE 操作。在处理分区表时,通常会调用 partition_needs_vacanalyze 函数来检查每个分区是否需要进行这些操作,而在处理普通表时,可能会调用 relation_needs_vacanalyze 函数。这两个函数的实现逻辑类似,但根据传入的参数和上下文,它们的应用范围有所不同。

      此外,细心的小伙伴可能会发现,partition_needs_vacanalyze 函数入参数没有 allowAnalyzeallowVacuum,而relation_needs_vacanalyze 函数却有这两个参数,这是为什么呢?

    • relation_needs_vacanalyze 函数用于确定单个表是否需要执行自动化的 VACUUMANALYZE 操作,因此它需要额外的参数来控制是否允许执行这些操作,即 allowAnalyzeallowVacuum 参数。这些参数用于根据表的当前状态和配置选项,以及是否启用了自动化操作来决定是否执行这些操作
    • 相比之下,partition_needs_vacanalyze 函数用于确定分区表的分区是否需要执行自动化的 VACUUMANALYZE 操作。由于分区表的自动化操作通常是由其父表的自动化操作控制的,因此它不需要额外的参数来控制是否允许执行这些操作。相反,它根据分区的当前状态、统计信息和其他因素来确定是否需要执行操作。

    案例

    1. 修改/data文件中的 autovacuum_naptime 参数。

      autovacuum_naptime 是数据库中的一个配置参数,它用于控制自动化 VACUUM 进程之间的休眠时间。具体来说,这个参数表示两次自动化 VACUUM 运行之间的时间间隔。这里为了快速看到效果,我们将时间修改为 3 秒,这意味着每隔 3 秒,自动化 VACUUM 进程会尝试运行一次。

    AUTOVACUUM PARAMETERS
    #------------------------------------------------------------------------------
    
    #autovacuum = off                       # Enable autovacuum subprocess?  default value is 'off'
                                            # requires track_counts to also be on.
    #log_autovacuum_min_duration = -1       # -1 disables, 0 logs all actions and
                                            # their durations, > 0 logs only
                                            # actions running at least this number
                                            # of milliseconds.
    #autovacuum_max_workers = 3             # max number of autovacuum subprocesses
                                            # (change requires restart)
    autovacuum_naptime = 3s                 # time between autovacuum runs
    #autovacuum_vacuum_threshold = 50       # min number of row updates before
                                            # vacuum
    #autovacuum_analyze_threshold = 50      # min number of row updates before
                                            # analyze
    #autovacuum_vacuum_scale_factor = 0.2   # fraction of table size before vacuum
    #autovacuum_analyze_scale_factor = 0.1  # fraction of table size before analyze
    #autovacuum_freeze_max_age = 200000000  # maximum XID age before forced vacuum
                                            # (change requires restart)
    #autovacuum_vacuum_cost_delay = 20ms    # default vacuum cost delay for
                                            # autovacuum, in milliseconds;
                                            # -1 means use vacuum_cost_delay
    #autovacuum_vacuum_cost_limit = -1      # default vacuum cost limit for
                                            # autovacuum, -1 means use
                                            # vacuum_cost_limit
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2. 创建存储表,执行以下 SQL 语句

    postgres=# create table t1 (id int) with (orientation = column);
    CREATE TABLE
    postgres=# insert into t1 select generate_series(1,1000);
    INSERT 0 1000
    postgres=# select * from t1 limit 10;
     id
    ----
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
    (10 rows)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3. 等待 3 秒后执行自动分析并查看统计信息

      可以通过 pg_stat_user_tables 表看到上次执行 autoanalyze 的时间。

    postgres=# select * from pg_stat_user_tables where relid = 't1'::regclass::oid;
     relid | schemaname | relname | seq_scan | seq_tup_read | idx_scan | idx_tup_fetch | n_tup_ins | n_tup_upd | n_tup_del | n_tup_hot_upd | n_live_tup | n_dead_tup
    | last_vacuum | last_autovacuum |         last_analyze          |       last_autoanalyze        | vacuum_count | autovacuum_count | analyze_count | autoanalyze_c
    ount |       last_data_changed
    -------+------------+---------+----------+--------------+----------+---------------+-----------+-----------+-----------+---------------+------------+------------
    +-------------+-----------------+-------------------------------+-------------------------------+--------------+------------------+---------------+--------------
    -----+-------------------------------
     49232 | public     | t1      |        0 |            0 |          |               |      1000 |         0 |         0 |             0 |       1000 |          0
    |             |                 | 2023-10-08 17:51:34.854412+08 | 2023-10-08 17:51:34.854412+08 |            0 |                0 |             1 |
       1 | 2023-10-08 17:47:36.488185+08
    (1 row)
    
    postgres=# select last_autoanalyze from pg_stat_user_tables where relid = 't1'::regclass::oid;
           last_autoanalyze
    -------------------------------
     2023-10-08 17:51:34.854412+08
    (1 row)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    Mac电脑其他文件占用超过一大半的内存如何清理?
    leetcode - 253. Meeting Rooms II
    快速入门JWT+Redis实现Token验证
    Python实现模块热加载
    allure测试报告应用和实践
    计算机毕业设计之java+ssm社会保险养老系统
    秦简总经理王建忠:论都江堰泛计算操作系统的商业价值
    MySQL进阶-事务及索引
    数据中台避不开的话题,数据服务到底是什么?
    【云原生 • Kubernetes】kubernetes 核心技术 - Pod
  • 原文地址:https://blog.csdn.net/qq_43899283/article/details/133681550