• 【PostgreSQL内核学习(十七)—— (AutoAnalyze)】


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

    概述

      AutoAnalyze 是一种自动统计信息收集和优化功能,用于数据库管理系统中。其主要目的是在数据库中定期收集表的统计信息,以便查询优化器能够生成更有效的执行计划。以下是有关 AutoAnalyze 的详细信息:

    1. 自动统计信息收集 AutoAnalyze自动定期检查数据库中的表,并确定哪些表需要更新统计信息。这些统计信息包括行数唯一值数量最小值最大值等,这些信息对于查询优化非常重要。
    2. 查询优化 收集的统计信息可帮助查询优化器更好地估算查询成本,从而选择更优的执行计划。这可以提高查询性能减少查询的执行时间
    3. 避免过度分析 AutoAnalyze 会智能地选择哪些表需要进行统计信息收集,以避免不必要的分析。它通常会关注哪些表的数据发生了变化,以及哪些表的统计信息可能已经过时
    4. 自动化 一旦启用,AutoAnalyze 会自动在后台执行无需手动干预。这使得数据库管理员的工作更加轻松,不需要手动分析每个表。

      此外,我们先前学习过 ANALYZE, 该命令是一个手动命令,它允许数据库管理员明确指示系统去收集特定表的统计信息。相比之下,AutoAnalyze 是自动执行的 ,系统会自动选择需要分析的表,并在后台进行统计信息收集
      AutoAnalyze 通常用于处理大量表,以确保它们的统计信息保持最新。而 ANALYZE 通常用于手动分析那些特殊需要或数据分布变化较大的表。本文,我们则来学习 AutoAnalyze 的相关源码实现。

    AutoAnaProcess 类

      AutoAnaProcess 类用于支持自动分析Auto-Analyze功能,该功能用于执行表的自动统计信息收集和优化。该类用于管理执行自动分析操作,它包含了与数据库连接SQL 查询命令的执行以及资源管理相关的方法和属性
      AutoAnaProcess 类函数源码如下:(路径:src/include/optimizer/autoanalyzer.h

    /* Auto-Analyze */
    // 自动分析进程类,用于处理自动分析操作
    class AutoAnaProcess : public BaseObject {
    public:
        // 构造函数,接收一个关系对象作为参数
        AutoAnaProcess(Relation rel);
    
        // 析构函数
        ~AutoAnaProcess();
    
        // 静态方法,用于执行自动分析操作,接收一个关系对象作为参数
        static bool runAutoAnalyze(Relation rel);
    
        // 静态方法,用于取消自动分析操作
        static void cancelAutoAnalyze();
    
    protected:
        // PostgreSQL数据库连接对象
        PGconn* m_pgconn;
    
        // PostgreSQL查询结果对象
        PGresult* m_res;
    
        // 存储SQL查询命令的列表
        List* m_query;
    
    private:
        // 静态方法,用于检查自动分析的条件,接收一个关系对象作为参数
        static bool check_conditions(Relation rel);
    
        // 执行自动分析操作的私有方法
        bool run();
    
        // 执行SQL查询命令的私有方法,接收一个SQL命令字符串作为参数
        bool executeSQLCommand(char* cmd);
    
        // 清理资源的静态方法
        static void tear_down();
    };
    
    • 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

      在这个类中,包含了类的构造函数析构函数runAutoAnalyze 静态方法cancelAutoAnalyze 静态方法以及一些需要用到的属性方法。接下来我们分别来看一看该类中这些相关函数的实现吧。

    AutoAnaProcess 函数

      AutoAnaProcessAutoAnaProcess 类的构造函数,主要用于初始化自动分析处理对象的属性设置需要执行的自动分析相关的 SQL 命令。该函数的执行过程如下所示:

    1. AutoAnaProcess::AutoAnaProcess(Relation rel) : m_pgconn(NULL), m_res(NULL):
    • 这是构造函数的定义,接受一个名为Relation rel的参数,表示要执行自动分析的数据库表
    • m_pgconnm_resAutoAnaProcess 类的私有属性,用于管理与数据库连接和查询结果相关的信息。在构造函数中,它们都被初始化为 NULL
    1. m_query = NIL;:
    • m_query 是一个列表,用于存储将要执行的 SQL 查询命令。在构造函数中,它被初始化为空列表
    1. StringInfoData str_lockwait_timeout;:
    • str_lockwait_timeout 是一个 StringInfoData 结构,用于存储关于锁等待超时的配置信息
    1. initStringInfo(&str_lockwait_timeout);:
    • initStringInfo 是一个宏,用于初始化 str_lockwait_timeout,使其可以存储字符串信息
    1. appendStringInfo(&str_lockwait_timeout, “set lockwait_timeout=%d ;”, AUTOANALYZE_LOCKWAIT_TIMEOUT);:
    • appendStringInfo 用于将字符串 “set lockwait_timeout=某个值 ;” 添加到 str_lockwait_timeout 中,该值由常量 AUTOANALYZE_LOCKWAIT_TIMEOUT 决定。这个 SQL 命令用于设置锁等待超时时间
    1. m_query = lappend(m_query, str_lockwait_timeout.data);:
    • 这一行代码将 str_lockwait_timeout 中的 SQL 命令添加到 m_query 列表中。
    1. 类似的过程重复了两次,用于设置另外两个配置参数:
    • str_allow_concurrent_tuple_update 用于设置是否允许并发元组更新
    • str_max_query_retry_times 用于设置最大查询重试次数
    1. 最后,通过以下代码设置了执行自动分析的 SQL 命令:
    StringInfoData str_analyze_command;
    initStringInfo(&str_analyze_command);
    appendStringInfo(&str_analyze_command,
       "analyze %s.%s ;",
       quote_identifier(get_namespace_name(rel->rd_rel->relnamespace)),
       quote_identifier(NameStr(rel->rd_rel->relname)));
    m_query = lappend(m_query, str_analyze_command.data);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 这部分代码构建了一个 analyze 命令,该命令用于执行自动分析操作。它包括了要分析的表的模式名和表名,通过调用 quote_identifier 函数来确保这些标识符的正确引用。
    • 最后,这个 SQL 命令被添加到 m_query 列表中,以便后续执行

      函数源码如下所示:(路径:src/gausskernel/optimizer/util/autoanalyzer.cpp

    /* Constructor */
    // 构造函数
    AutoAnaProcess::AutoAnaProcess(Relation rel) : m_pgconn(NULL), m_res(NULL)
    {
        m_query = NIL;  // 初始化查询命令列表为一个空列表
    
        StringInfoData str_lockwait_timeout;  // 创建一个用于存储锁等待超时配置的字符串信息结构
        initStringInfo(&str_lockwait_timeout);  // 初始化字符串信息结构
    
        // 向字符串信息结构中追加锁等待超时的设置命令,AUTOANALYZE_LOCKWAIT_TIMEOUT是一个常量,表示锁等待超时的时间
        appendStringInfo(&str_lockwait_timeout, "set lockwait_timeout=%d ;", AUTOANALYZE_LOCKWAIT_TIMEOUT);
        m_query = lappend(m_query, str_lockwait_timeout.data);  // 将设置命令添加到查询命令列表中
    
        StringInfoData str_allow_concurrent_tuple_update;  // 创建一个用于存储是否允许并发元组更新的字符串信息结构
        initStringInfo(&str_allow_concurrent_tuple_update);  // 初始化字符串信息结构
    
        // 向字符串信息结构中追加允许并发元组更新的设置命令
        appendStringInfo(&str_allow_concurrent_tuple_update, "set allow_concurrent_tuple_update='off';");
        m_query = lappend(m_query, str_allow_concurrent_tuple_update.data);  // 将设置命令添加到查询命令列表中
    
        StringInfoData str_max_query_retry_times;  // 创建一个用于存储最大查询重试次数的字符串信息结构
        initStringInfo(&str_max_query_retry_times);  // 初始化字符串信息结构
    
        // 向字符串信息结构中追加最大查询重试次数的设置命令
        appendStringInfo(&str_max_query_retry_times, "set max_query_retry_times=0;");
        m_query = lappend(m_query, str_max_query_retry_times.data);  // 将设置命令添加到查询命令列表中
    
        StringInfoData str_analyze_command;  // 创建一个用于存储执行自动分析的SQL命令的字符串信息结构
        initStringInfo(&str_analyze_command);  // 初始化字符串信息结构
    
        // 构建执行自动分析的SQL命令,包括要分析的表的模式名和表名,通过quote_identifier函数确保标识符的正确引用
        appendStringInfo(&str_analyze_command,
            "analyze %s.%s ;",
            quote_identifier(get_namespace_name(rel->rd_rel->relnamespace)),
            quote_identifier(NameStr(rel->rd_rel->relname)));
        m_query = lappend(m_query, str_analyze_command.data);  // 将执行命令添加到查询命令列表中
    }
    
    • 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

    AutoAnaProcess::executeSQLCommand 函数

      executeSQLCommand 函数的主要作用是执行传入的 SQL 命令字符串,并返回执行结果的布尔值。它还会记录执行成功或失败的信息,并在执行期间允许取消或终止请求的处理
      函数首先保存当前线程的中断状态,以允许在等待期间处理取消或终止请求,然后执行 SQL 查询检查执行结果的状态,如果成功则记录成功信息,如果失败则记录错误信息,并最后返回执行结果的布尔值。这个函数通常用于执行数据库操作,如自动分析Auto-Analyze)等。
      函数源码如下:(路径:src/gausskernel/optimizer/util/autoanalyzer.cpp

    bool AutoAnaProcess::executeSQLCommand(char* queryString)
    {
        // 初始化执行结果标志为false
        bool result = false;
        
        // 保存原始的ImmediateInterruptOK标志,用于在等待期间处理取消或终止请求
        bool ImmediateInterruptOK_Old = t_thrd.int_cxt.ImmediateInterruptOK;
        
        /* 允许在等待期间处理取消或终止请求 */
        t_thrd.int_cxt.ImmediateInterruptOK = true;
        
        // 检查是否有中断请求
        CHECK_FOR_INTERRUPTS();
        
        // 执行 SQL 查询并将结果存储在m_res中
        m_res = PQexec(m_pgconn, queryString);
        
        // 恢复原始的ImmediateInterruptOK标志
        t_thrd.int_cxt.ImmediateInterruptOK = ImmediateInterruptOK_Old;
    
        // 检查执行结果的状态
        if (PQresultStatus(m_res) != PGRES_COMMAND_OK) {
            // 如果执行失败,记录失败信息和错误信息
            elog(DEBUG2, "[AUTO-ANALYZE] autoanalyze failed: %s, error: %s", queryString, PQerrorMessage(m_pgconn));
        } else {
            // 如果执行成功,设置执行结果标志为true,并记录成功信息
            result = true;
            elog(DEBUG2, "[AUTO-ANALYZE] autoanalyze success: %s", queryString);
        }
        
        // 清理执行结果对象
        PQclear(m_res);
        m_res = NULL;
        
        // 返回执行结果的布尔值
        return result;
    }
    
    • 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

    以下是 ExecutorRun 函数的执行过程的解释:

    1. 首先,函数接收一个字符串参数 queryString,该参数包含要执行的 SQL 命令
    2. 函数开始时,初始化一个布尔型变量 result,用于表示 SQL 命令的执行结果,默认值为 false
    3. 接着,函数保存当前线程ImmediateInterruptOK 标志的原始值,并将其设置为 true。这允许在等待期间处理取消或终止请求
    4. 然后,函数调用 CHECK_FOR_INTERRUPTS() 检查是否有中断请求,以确保可以响应取消请求
    5. 接下来,函数使用 PQexec 函数执行传入的 queryString,并将执行结果存储在成员变量 m_res 中。这个结果可能是执行成功的命令返回的信息,或者执行失败的错误信息
    6. 函数恢复原始的 ImmediateInterruptOK 标志值,以确保不再处理取消或终止请求
    7. 接着,函数检查执行结果的状态。如果执行失败,函数记录失败信息和错误信息到日志中。
    8. 如果执行成功,函数将 result 设置为 true,表示执行成功,并记录成功信息到日志中
    9. 最后,函数清理执行结果对象 m_res,将其置为 NULL
    10. 最终,函数返回 result,表示 SQL 命令的执行结果,如果执行成功,则为 true;如果执行失败,则为 false

    AutoAnaProcess::runAutoAnalyze 函数

      runAutoAnalyze 函数的主要作用是触发自动分析(Auto-Analyze)过程,用于更新数据库中表的统计信息以优化查询性能它检查是否满足自动分析的条件获取可用的自动分析进程执行自动分析,并记录自动分析的执行时间。函数的功能包括条件检查进程管理自动分析的执行,以及记录自动分析时间信息
      runAutoAnalyze 函数非常重要,因为它实现了数据库自动分析(Auto-Analyze)的关键逻辑。自动分析是数据库优化的一部分,它负责监测表的数据变化并定期进行统计信息的收集,以便查询优化器可以更好地选择执行计划。通过这个函数,数据库能够自动化地维护和更新这些统计信息,从而保持查询性能的稳定性和高效性。因此,该函数的正确执行对于数据库的性能和稳定性至关重要。
      runAutoAnalyze 函数源码如下所示:(路径:src/gausskernel/optimizer/util/autoanalyzer.cpp

    bool AutoAnaProcess::runAutoAnalyze(Relation rel)
    {
        // 获取自动分析进程是否可用的标志
        bool getProcess = false;
        // 记录自动分析的执行结果
        bool result = false;
        // 记录自动分析开始的时间
        TimestampTz start_time = 0;
    
        // 如果需要在 EXPLAIN ANALYZE 中显示自动分析的执行时间
        if (u_sess->analyze_cxt.autoanalyze_timeinfo) {
            // 获取当前时间作为自动分析的开始时间
            start_time = GetCurrentTimestamp();
        }
    
        // 第一步:检查是否满足自动分析的条件
        if (!check_conditions(rel)) {
            // 如果不满足条件,记录日志并返回执行结果为失败
            elog(DEBUG2, "[AUTO-ANALYZE] 检查自动分析条件失败: 表 \"%s\"", NameStr(rel->rd_rel->relname));
            return result;
        }
    
        // 第二步:获取可用的自动分析进程
        (void)LWLockAcquire(AutoanalyzeLock, LW_EXCLUSIVE);
        if (autoAnalyzeFreeProcess > 0) {
            // 如果存在可用进程,设置标志为可获取进程并减少可用进程数
            getProcess = true;
            autoAnalyzeFreeProcess--;
        }
        (void)LWLockRelease(AutoanalyzeLock);
    
        // 如果没有可用进程,记录日志并返回执行结果为失败
        if (!getProcess) {
            elog(DEBUG2, "[AUTO-ANALYZE] 没有可用的自动分析进程");
            return result;
        }
    
        // 第三步:执行自动分析
        Assert(u_sess->analyze_cxt.autoanalyze_process == NULL);
        // 创建自动分析进程
        u_sess->analyze_cxt.autoanalyze_process = New(CurrentMemoryContext) AutoAnaProcess(rel);
        // 执行自动分析,并记录执行结果
        result = u_sess->analyze_cxt.autoanalyze_process->run();
        // 清理自动分析进程资源
        tear_down();
    
        // 第四步:释放可用自动分析进程
        LWLockAcquire(AutoanalyzeLock, LW_EXCLUSIVE);
        autoAnalyzeFreeProcess++;
        LWLockRelease(AutoanalyzeLock);
    
        // 第五步:如果需要,在 EXPLAIN ANALYZE 中记录自动分析的执行时间
        if (u_sess->analyze_cxt.autoanalyze_timeinfo && result) {
            // 计算自动分析的执行时间
            long secs;
            long msecs;
            int usecs;
            TimestampDifference(start_time, GetCurrentTimestamp(), &secs, &usecs);
            msecs = usecs / 1000L;
            msecs = secs * 1000 + msecs;
            usecs = usecs % 1000;
    
            // 将自动分析的执行时间信息添加到相应的字符串中
            MemoryContext oldcontext = MemoryContextSwitchTo(u_sess->temp_mem_cxt);
            appendStringInfo(u_sess->analyze_cxt.autoanalyze_timeinfo,
                "\"%s.%s\" %ld.%03dms ",
                get_namespace_name(rel->rd_rel->relnamespace),
                NameStr(rel->rd_rel->relname),
                msecs,
                usecs);
            (void)MemoryContextSwitchTo(oldcontext);
        }
    
        // 返回自动分析的执行结果
        return result;
    }
    
    • 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

    以下是函数的执行过程简述:

    1. 首先,函数接收一个参数 rel,表示一个关系(表)
    2. 函数初始化了一些局部变量,包括 getProcess(用于表示是否获取到了可用的 Auto-Analyze 进程)、result(用于表示 Auto-Analyze 的执行结果是否成功)、start_time(用于记录 Auto-Analyze 开始时间)。
    3. 如果当前会话的上下文u_sess->analyze_cxt.autoanalyze_timeinfo)允许记录自动分析时间信息,函数记录 Auto-Analyze开始时间
    4. 接着,函数调用 check_conditions(rel) 检查是否满足执行自动分析的条件,如果不满足条件,则函数返回执行结果 resultfalse
    5. 如果条件满足,函数尝试获取一个可用的 Auto-Analyze 进程。它首先获取了自动分析锁(AutoanalyzeLock),并检查是否还有可用的 Auto-Analyze 进程。如果有可用的进程,则将 getProcess 设为 true,并减少可用进程的数量
    6. 如果没有可用的 Auto-Analyze 进程,函数返回执行结果 resultfalse,并记录日志,表示没有可用的进程
    7. 如果成功获取到 Auto-Analyze 进程,函数进入执行 Auto-Analyze 的步骤。首先,它确保当前会话的 autoanalyze_process 为空,然后创建一个新的 AutoAnaProcess 实例,传入 rel,并执行 Auto-Analyze
    8. 执行完 Auto-Analyze 后,函数执行 tear_down()清理资源
    9. 最后,函数释放 AutoanalyzeLock增加可用 Auto-Analyze 进程的数量,并在成功执行 Auto-Analyze允许记录自动分析时间信息的情况下,记录 Auto-Analyze耗时信息u_sess->analyze_cxt.autoanalyze_timeinfo
    10. 函数返回执行结果 result,表示 Auto-Analyze 的执行结果,如果执行成功,则为 true;如果执行失败没有可用进程,则为 false

    AutoAnaProcess::run 函数

      AutoAnaProcess::run 函数的作用是执行自动分析操作,其功能包括建立数据库连接、执行一系列自动分析操作的查询命令记录执行结果,并最终返回操作是否成功的结果。其函数源码如下:(路径:src/gausskernel/optimizer/util/autoanalyzer.cpp

    bool AutoAnaProcess::run()
    {
        char conninfo[CHAR_BUF_SIZE];  // 存储数据库连接信息的字符数组
        int ret;                        // 保存函数返回值
        bool result = false;            // 用于表示自动分析是否成功
        ListCell* lc = NULL;            // 用于遍历查询命令列表
    
        // 遍历自动分析操作的查询命令列表
        foreach (lc, m_query) {
            char* cmd = (char*)lfirst(lc); // 获取当前查询命令字符串
            // 记录自动分析操作的开始
            elog(DEBUG2, "[AUTO-ANALYZE] autoanalyze start: %s", cmd);
        }
    
        // 构建数据库连接信息字符串
        ret = snprintf_s(conninfo,
            sizeof(conninfo),
            sizeof(conninfo) - 1,
            "dbname=%s port=%d application_name='auto_analyze' enable_ce=1 ",
            get_and_check_db_name(u_sess->proc_cxt.MyDatabaseId, true),  // 获取当前数据库名
            g_instance.attr.attr_network.PostPortNumber);  // 获取数据库端口号
        securec_check_ss_c(ret, "\0", "\0");  // 安全检查
    
        // 连接数据库
        m_pgconn = PQconnectdb(conninfo);
    
        // 检查数据库连接是否成功
        if (PQstatus(m_pgconn) == CONNECTION_OK) {
            foreach (lc, m_query) {
                char* cmd = (char*)lfirst(lc); // 获取当前查询命令字符串
                // 执行自动分析操作的查询命令
                result = executeSQLCommand(cmd);
                if (!result) {
                    break;
                }
            }
        } else {
            // 记录连接数据库失败的信息
            elog(DEBUG2, "[AUTO-ANALYZE] connection to database failed: %s", PQerrorMessage(m_pgconn));
        }
    
        // 清理资源
        PQclear(m_res);    // 清理查询结果
        PQfinish(m_pgconn); // 关闭数据库连接
        m_res = NULL;
        m_pgconn = NULL;
        return result; // 返回自动分析操作是否成功的结果
    }
    
    • 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

    这个函数的作用是执行自动分析操作,其执行过程如下:

    1. 函数开始时,遍历存储在 m_query 列表中的查询命令,将每个命令打印到日志中,以便跟踪执行进度
    2. 然后,构建数据库连接信息字符串 conninfo,该字符串包括数据库名端口号应用程序名称以及其他参数。
    3. 使用 PQconnectdb 函数基于构建好的连接信息建立与数据库的连接(m_pgconn)
    4. 检查与数据库的连接状态是否正常PQstatus(m_pgconn) == CONNECTION_OK)。
    5. 如果连接成功,继续遍历 m_query 列表中的每个查询命令,依次执行这些命令,同时记录执行结果。
    6. 如果某个查询命令执行失败executeSQLCommand 返回 false),则中断执行,并将 result 设置为 false
    7. 如果与数据库的连接失败,将错误信息打印到日志中。
    8. 最后,清理资源,包括释放查询结果对象PQclear(m_res))、关闭与数据库的连接PQfinish(m_pgconn)),并将 m_resm_pgconn 置为 NULL
    9. 返回执行的结果,如果所有查询都成功执行,则 resulttrue,否则为 false

    AutoAnaProcess::check_conditions 函数

      在 AutoAnaProcess::runAutoAnalyze 函数中采用 AutoAnaProcess::check_conditions 函数检查是否满足自动分析的条件该函数主要用于确保执行自动分析的用户有足够的权限,并且不分析临时表以及在只读模式下的表。如果满足这些条件,函数返回 true允许执行自动分析操作。否则,返回 false不执行自动分析
      AutoAnaProcess::runAutoAnalyze 函数源码如下:(路径:src/gausskernel/optimizer/util/autoanalyzer.cpp

    /*
     * check_conditions
     * 检查执行自动分析的用户权限以及表是否为临时表
     */
    bool AutoAnaProcess::check_conditions(Relation rel)
    {
        /* 如果传入的关系(表)是无效的,直接返回false */
        if (!rel)
            return false;
    
        /* 如果传入的关系是临时表,直接返回false */
        if (RelationIsLocalTemp(rel))
            return false;
    
        /*
         * 如果关系处于只读模式(非数据重分布的情况),我们跳过对该关系的分析。
         */
        if (!u_sess->attr.attr_sql.enable_cluster_resize && RelationInClusterResizingReadOnly(rel))
            return false;
    
        // 使用ACL检查当前用户是否有执行VACUUM操作的权限
        AclResult aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_VACUUM);
        if (aclresult != ACLCHECK_OK && !(pg_class_ownercheck(RelationGetRelid(rel), GetUserId()) ||
                (pg_database_ownercheck(u_sess->proc_cxt.MyDatabaseId, GetUserId()) && !rel->rd_rel->relisshared))) {
            return false;
        }
    
        return true; // 如果所有条件都满足,返回true表示可以执行自动分析
    }
    
    • 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

    以下是函数的执行过程简述:

    1. 首先,函数接收一个名为 rel 的参数,这个参数是一个数据库表(关系)的指针,用于表示要进行自动分析的表
    2. 函数开始执行时,首先检查传入的表是否为无效表,如果是无效表(即空指针),则直接返回 false,表示不满足执行自动分析的条件
    3. 接下来,函数检查传入的表是否为临时表,如果是临时表,则同样返回 false,因为临时表通常不需要进行自动分析
    4. 如果传入的表不是临时表,函数会继续检查是否处于只读模式。如果表处于只读模式,通常意味着它是分布式数据库中的只读副本不需要进行自动分析。如果在只读模式下,函数也会返回 false
    5. 最后,函数会进行访问控制列表(ACL)检查,以确保当前用户具有执行 VACUUM 操作的权限。如果用户没有这个权限,并且用户不是表的所有者,也不是数据库的所有者(只有在表不是共享表的情况下),则函数返回 false
    6. 如果所有条件都满足,即表有效不是临时表不处于只读模式,且用户有足够的权限,函数将返回 true,表示可以执行自动分析操作

    AutoAnaProcess::cancelAutoAnalyze 函数

      AutoAnaProcess::cancelAutoAnalyze 函数的作用是取消正在执行的自动分析过程。它通过释放相关资源增加可用的自动分析进程数量,以及清除执行计划信息来中止自动分析,从而管理和控制自动分析的执行
      cancelAutoAnalyze 函数源码如下:(路径:src/gausskernel/optimizer/util/autoanalyzer.cpp

    void AutoAnaProcess::cancelAutoAnalyze()
    {
        /* 如果当前存在正在执行的自动分析进程 */
        if (u_sess->analyze_cxt.autoanalyze_process != NULL) {
            /* 执行资源清理操作 */
            tear_down();
    
            /* 释放自动分析进程资源,增加可用的自动分析进程数量 */
            (void)LWLockAcquire(AutoanalyzeLock, LW_EXCLUSIVE);
            autoAnalyzeFreeProcess++;
            LWLockRelease(AutoanalyzeLock);
        }
    
        /* 清除自动分析的执行计划信息,如果存在的话 */
        if (u_sess->analyze_cxt.autoanalyze_timeinfo != NULL) {
            /* 释放执行计划信息的内存 */
            pfree_ext(u_sess->analyze_cxt.autoanalyze_timeinfo->data);
            pfree_ext(u_sess->analyze_cxt.autoanalyze_timeinfo);
            u_sess->analyze_cxt.autoanalyze_timeinfo = NULL;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    该函数用于取消正在执行的自动分析过程,其执行过程如下

    1. 首先检查是否存在正在执行的自动分析进程u_sess->analyze_cxt.autoanalyze_process不为空)。
    2. 如果存在正在执行的自动分析进程,执行资源清理操作tear_down 函数),包括关闭与数据库的连接等。
    3. 释放自动分析进程资源,并增加可用的自动分析进程数量(通过获取和释放锁 AutoanalyzeLock 来实现并发安全性)。
    4. 接着,检查是否存在自动分析的执行计划信息u_sess->analyze_cxt.autoanalyze_timeinfo 不为空)。
    5. 如果存在执行计划信息释放执行计划信息的内存,将其清空

    AutoAnaProcess::~AutoAnaProcess 函数

      ~AutoAnaProcess析构函数,用于释放AutoAnaProcess 对象所占用的资源,包括关闭数据库连接清理查询结果释放查询命令列表的资源,并将相关指针和列表重置为空或 NULL,以确保资源的正确释放避免内存泄漏。函数源码如下:(路径:src/gausskernel/optimizer/util/autoanalyzer.cpp

    AutoAnaProcess::~AutoAnaProcess()
    {
        if (t_thrd.utils_cxt.CurrentResourceOwner == NULL) {
            m_res = NULL;
        }
        PQclear(m_res);    // 清理查询结果
        PQfinish(m_pgconn); // 关闭数据库连接
        if (m_query != NIL) {
            list_free_deep(m_query); // 递归释放查询命令列表的资源
        }
        m_pgconn = NULL; // 重置数据库连接指针
        m_res = NULL;    // 重置查询结果指针
        m_query = NIL;   // 重置查询命令列表
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    基于MIPS32的五级流水及中断控制器设计
    计算机网络——第五章网络层笔记(5)
    【基础讲解】基于matpower的电力系统潮流计算(Matlab代码实现)
    零基础能学大数据技术吗?学完能找到工作吗?
    【云原生】kubernetes学习之资源(对象)控制器概述---概念和实战(五)
    leetCode 解法集锦
    Clickhouse—时间窗口函数
    LCP创建bond接口
    LeetCode13——罗马数字转整数
    线上展厅的作用 广州商迪
  • 原文地址:https://blog.csdn.net/qq_43899283/article/details/133632671