海山数据库(He3DB)技术分享:He3DB GUC配置参数

背景

   He3DB for PostgreSQL是受Aurora论文启发,基于开源数据库PostgreSQL 改造的数据库产品。架构上实现计算存储分离,并进一步支持数据的冷热分层,大幅提升产品的性价比。

   He3DB for PostgreSQL中GUC是一套用于控制数据库配置的参数系统。GUC 参数允许数据库管理员根据需要调整数据库的行为和性能。

一、GUC介绍

1.1 参数分类

   GUC(Grand Unified Configuration)参数,其实指的就是pg中的各类参数。如果按生效时分类,主要有以下7类(guc.h文件):

1.typedef enum  
2.{  
3.    PGC_INTERNAL,   // 只能通过内部进程设置的参数,用户不能设置  
4.    PGC_POSTMASTER, // 只能在postmaster启动时,通过读取配置文件或解析命令行参数配置(重启生效)  
5.    PGC_SIGHUP,     // 只能在postmaster启动时,或者改变配置文件后发送SIGHUP信号设置(重启或执行pg_reload_conf();生效)  
6.    PGC_SU_BACKEND, //只能在postmaster启动时,或者客户端新建连接时生效,已建立的连接会忽略(这类参数只能由超级用户设置)  
7.    PGC_BACKEND,    // 只能在postmaster启动时,或者客户端新建连接时生效,已建立的连接会忽略  
8.    PGC_SUSET,      // 只能在postmaster启动时,或者由超级用户通过SQL(set命令)设置  
9.    PGC_USERSET     // 任何用户任何时候均可配置  
10.} GucContext;  

1.2 参数来源

   参数来源由GucSource描述(guc.h文件),按照优先级从低到高的顺序排列,相同参数优先级更低或相等时才会生效。

1.typedef enum  
2.{  
3.    PGC_S_DEFAULT,              /* 默认值 */  
4.    PGC_S_DYNAMIC_DEFAULT,      /* 通过初始化计算得到 */  
5.    PGC_S_ENV_VAR,              /* 来自环境变量 */  
6.    PGC_S_FILE,                 /* 来自postgresql.conf */  
7.    PGC_S_ARGV,                 /* 来自postmaster命令行 */  
8.    PGC_S_GLOBAL,               /* 数据库全局设置 */  
9.    PGC_S_DATABASE,             /* 数据库安装时设置 */  
10.    PGC_S_USER,                 /* 用户设置 */  
11.    PGC_S_DATABASE_USER,        /* 用户在单个数据库中的设置*/  
12.    PGC_S_CLIENT,               /* 通过客户端连接请求传送过来的数据包设置 */  
13.    PGC_S_OVERRIDE,             /* 特殊场景强制覆盖默认值 */  
14.    PGC_S_INTERACTIVE,          /* 仅作为错误报告的分界线 */  
15.    PGC_S_TEST,                 /* 仅用作测试 */  
16.    PGC_S_SESSION               /* SET命令设置 */  
17.} GucSource;  // 以上来源优先级从低到高  

1.3 参数组成

   每种类型的GUC参数都有两部分组成:共性部分+特性部分。
   共性部分:由config_generic结构体描述
   特性部分:每种具体数据类型(boolean,int,float,string)的参数都有对应结构体(例如config_int),且结构体的第一项都是指向共性部分的指针。
   共性部分代码(以下均在guc_tables.h)

1.struct config_generic  
2.{  
3.    /* constant fields, must be set correctly in initial value 
4.常量区域,必须正确设置为初始化值 */  
5.    const char *name;           /* 参数名 - MUST BE FIRST */  
6.    GucContext  context;        /* 参数类型(按生效时间,即前面提到的GucContext) */  
7.    enum config_group group;    /* 参数分组,使得它们可以在用户界面(如 pg_settings 视图)中以分组的方式展示,方便用户浏览和理解 */  
8.    const char *short_desc;     /* 简单描述 */  
9.    const char *long_desc;      /* 详细描述 */  
10.    int         flags;          /* 标志位, see guc.h */  
11.   
12.    /* variable fields, initialized at runtime 
13.变量区域,在运行时初始化 */  
14.    enum config_type vartype;   /* 参数值数据类型 int,bool等 */  
15.    int         status;         /* 参数状态 */  
16.    GucSource   source;         /* 参数来源 */  
17.    GucSource   reset_source;   /* 参数值为reset_value时的参数来源*/  
18.    GucContext  scontext;       /* 参数设置上下文 */  
19.    GucContext  reset_scontext; /* 参数值为reset_value时的上下文 */  
20.    GucStack   *stack;          /* 堆栈,用于保存前一个值 */  
21.    void       *extra;          /* "extra" pointer 指向当前真实值 */  
22.    char       *last_reported;  /* 如果变量是 GUC_REPORT 类型,这个指针存储了最后一次发送给客户端的值(如果还没有发送,则为 NULL */  
23.    char       *sourcefile;     /* 配置所在源文件 */  
24.    int         sourceline;     /* 在源文件中的行号 */  
25.};  

按数据类型分类如下:

1.enum config_type  
2.{  
3.    PGC_BOOL,  
4.    PGC_INT,  
5.    PGC_REAL,  // 实数  
6.    PGC_STRING,  
7.    PGC_ENUM  
8.};  

特性部分,以config_int为例

1.struct config_int  
2.{  
3.    /* 第一部分一定指向共性结构 */  
4.    struct config_generic gen;  
5.   
6.    /* constant fields, must be set correctly in initial value: */  
7.    int        *variable;   /* 参数当前被设置值 */  
8.    int         boot_val;   /* 参数初始值 */  
9.    int         min;         /* 参数最小值 */  
10.    int         max;         /* 参数最大值 */  
11.    /*三个钩子函数 */ 
12.    GucIntCheckHook check_hook;   /*检查钩子 */  
13.    GucIntAssignHook assign_hook;  /*赋值钩子 */  
14.    GucShowHook show_hook;       /*展示钩子 */  
15.    /* variable fields, initialized at runtime: */  
16.    int         reset_val;    /*存储了 GUC 变量的重置值 */ 
17.    void       *reset_extra;  /*指向额外的数据,这些数据可以在重置 GUC 变量时使用 */ 
18.};  

二、GUC参数配置过程

参数配置基本过程

  1. 初始化GUC参数:将参数设置为默认值
  2. 解析postmaster命令行参数:根据postmaster命令行参数进行配置
  3. 读取参数文件:根据postgresql.conf文件中的设置值再次配置

主要函数调用:

1.postmasterMain();  //主入口点,启动 PostgreSQL 服务  
2.  |->InitializeGUCOptions(); //初始化全局配置参数(GUC)  
3.     |->build_guc_variables();  // 构建 GUC 变量的内部数据结构  
4.     |->InitializeOneGUCOption(); // 初始化单个 GUC 选项的默认值  
5.     |->SetConfigOption();         //设置相应的 GUC 选项  
6.     |->InitializeGUCOptionsFromEnvironment();  // 从环境变量中初始化 GUC 选项  
7.  |->getopt();                            // 解析命令行参数  
8.     |->SetConfigOption();             //设置相应的 GUC 选项  
9.  |->SelectConfigFiles();            //确定配置文件的位置  
10.     |->ProcessConfigFile();        // 处理配置文件  
11.     |->SetConfigOption();            //设置相应的 GUC 选项  

2.1 GUC初始化

   GUC的初始化主要由函数InitializeGUCOptions()(位于guc.c)实现。

1.void InitializeGUCOptions(void)  
2.{  
3.    int i; /* 声明循环计数器变量 */  
4.  
5.    /* 
6.     * 初始化时区处理, 
7.     * 以确保在设置 log_line_prefix 之前时区处理是可用的。 
8.     */  
9.    pg_timezone_initialize();  
10.  
11.    /* 
12.     * 构建所有GUC变量的排序数组, 
13.     * 这有助于后续的查找和初始化。 
14.     */  
15.    build_guc_variables();  
16.  
17.    /* 
18.     * 遍历所有GUC变量,使用编译时的默认值进行初始化, 
19.     * 并设置必要的状态字段。 
20.     */  
21.    for (i = 0; i < num_guc_variables; i++)  
22.    {  
23.        InitializeOneGUCOption(guc_variables[i]); /* 初始化单个GUC选项 */  
24.    }  
25.  
26.    /* 重置GUC变更标志,初始时没有变更 */  
27.    guc_dirty = false;  
28.  
29.    /* 初始时禁用报告功能 */  
30.    reporting_enabled = false;  
31.  
32.    /* 
33.     * 防止从非交互式来源覆盖事务模式设置, 
34.     * 这些设置对数据库的一致性和隔离级别至关重要。 
35.     */  
36.    SetConfigOption("transaction_isolation", "read committed", PGC_POSTMASTER, PGC_S_OVERRIDE);  
37.    SetConfigOption("transaction_read_only", "no", PGC_POSTMASTER, PGC_S_OVERRIDE);  
38.    SetConfigOption("transaction_deferrable", "no", PGC_POSTMASTER, PGC_S_OVERRIDE);  
39.  
40.    /* 
41.     * 处理来自环境变量的GUC参数默认值, 
42.     * 这允许系统管理员通过环境变量来设置某些参数的默认值。 
43.     */  
44.    InitializeGUCOptionsFromEnvironment();  
45.}  

   InitializeGUCOptions 函数在 He3DB中通过调用 pg_timezone_initialize 来初始化时区处理,使用 build_guc_variables 构建所有 GUC 变量的排序数组,然后通过循环和 InitializeOneGUCOption 对每个变量应用默认值。此外,它利用 SetConfigOption 锁定事务模式参数以防止非交互式覆盖,并调用 InitializeGUCOptionsFromEnvironment 从环境变量中导入额外的参数值,从而确保在服务器启动时所有关键的运行时配置参数被正确设置。

2.1.1build_guc_variables

1.void build_guc_variables(void)  
2.{  
3.    int size_vars; /* 存储分配的GUC变量数组的大小 */  
4.    int num_vars = 0; /* 计数找到的GUC变量数量 */  
5.    struct config_generic **guc_vars; /* 指向GUC变量数组的指针 */  
6.    int i; /* 循环计数器 */  
7.  
8.    /* 遍历布尔类型的GUC变量,设置变量类型并计数 */  
9.    for (i = 0; ConfigureNamesBool[i].gen.name; i++)  
10.    {  
11.        struct config_bool *conf = &ConfigureNamesBool[i];  
12.        conf->gen.vartype = PGC_BOOL; /* 设置变量类型为布尔型 */  
13.        num_vars++; /* 增加变量计数 */  
14.    }  
15.  
16.    /* 用相同的方法处理整型、浮点型、字符串型和枚举型的GUC变量 */  
17.    // ...(其他类型的GUC变量处理代码类似,省略以节省空间)  
18.  
19.    /* 
20.     * 创建带有25%余量的GUC变量数组 
21.     * 这样做是为了留出空间,以便在运行时添加更多的变量 
22.     */  
23.    size_vars = num_vars + num_vars / 4;  
24.  
25.    /* 分配内存以存储GUC变量指针 */  
26.    guc_vars = (struct config_generic **) guc_malloc(FATAL, size_vars * sizeof(struct config_generic *));  
27.  
28.    /* 将前面计数的GUC变量指针存储到新分配的数组中 */  
29.    num_vars = 0;  
30.    for (i = 0; ConfigureNamesBool[i].gen.name; i++)  
31.        guc_vars[num_vars++] = &ConfigureNamesBool[i].gen;  
32.    // ...(其他类型的GUC变量赋值代码类似)  
33.  
34.    /* 如果之前已经分配过guc_variables,释放旧的内存 */  
35.    if (guc_variables)  
36.        free(guc_variables);  
37.  
38.    /* 更新guc_variables为新分配的数组 */  
39.    guc_variables = guc_vars;  
40.    num_guc_variables = num_vars; /* 更新GUC变量的数量 */  
41.    size_guc_variables = size_vars; /* 更新分配的数组大小 */  
42.  
43.    /* 使用qsort函数对GUC变量数组进行排序 */  
44.    qsort((void *) guc_variables, num_guc_variables,  
45.          sizeof(struct config_generic *), guc_var_compare);  
46.}  

   InitializeGUCOptions()首先调用build_guc_variables函数来统计参数个数并分配相应的config_generic类型的全局指针数组guc_variables以保存每个参数结构体的地址,并且对该数组进行排序。由于参数是通过全局静态数组ConfigureNamesBool、ConfigureNamesInt、ConfigureNamesReal、ConfigureNamesString、ConfigureNamesEnum 存储的,因此在build_gue_variables函数中只需要遍历相应的数组,统计参数的个数并将参数结构体中config_generic域的参数vartype设置为相应的参数类型。当遍历完所有参数后,根据总的参数个数分配config_generic指针数组guc_vars,然后再次遍历静态参数数组,将每个参数结构的首地址保存到guc_vars数组中(这里分配的数组个数为当前参数总数的1.25倍,主要是为了方便以后参数的扩充)。接着将全局变量guc_variables也指向guc_vars数组。最后通过快速排序法把guc_variables按照参数名进行排序。

2.1.2 initializeGUCOptions

1.static void InitializeOneGUCOption(struct config_generic *gconf)  
2.{  
3.    /* 初始化GUC选项的通用字段 */  
4.    gconf->status = 0;  
5.    gconf->source = PGC_S_DEFAULT; /* 设置来源为默认 */  
6.    gconf->reset_source = PGC_S_DEFAULT; /* 设置重置来源为默认 */  
7.    gconf->scontext = PGC_INTERNAL; /* 设置上下文为内部 */  
8.    gconf->reset_scontext = PGC_INTERNAL; /* 设置重置上下文为内部 */  
9.    gconf->srole = BOOTSTRAP_SUPERUSERID; /* 设置角色为启动超级用户ID */  
10.    gconf->reset_srole = BOOTSTRAP_SUPERUSERID; /* 设置重置角色为启动超级用户ID */  
11.    gconf->stack = NULL; /* 初始化堆栈为NULL */  
12.    gconf->extra = NULL; /* 初始化额外数据为NULL */  
13.    gconf->last_reported = NULL; /* 初始化最后报告的值为NULL */  
14.    gconf->sourcefile = NULL; /* 初始化源文件为NULL */  
15.    gconf->sourceline = 0; /* 初始化源代码行为0 */  
16.  
17.    /* 根据GUC变量类型进行不同的初始化操作 */  
18.    switch (gconf->vartype)  
19.    {  
20.        /* 布尔型GUC变量的初始化 */  
21.        case PGC_BOOL:  
22.        {  
23.            struct config_bool *conf = (struct config_bool *) gconf;  
24.            bool newval = conf->boot_val; /* 使用启动值 */  
25.            void *extra = NULL;  
26.  
27.            /* 调用检查钩子函数,如果存在 */  
28.            if (!call_bool_check_hook(conf, &newval, &extra, PGC_S_DEFAULT, LOG))  
29.                elog(FATAL, "failed to initialize %s to %d", conf->gen.name, (int) newval);  
30.  
31.            /* 如果存在赋值钩子函数,则调用 */  
32.            if (conf->assign_hook)  
33.                conf->assign_hook(newval, extra);  
34.  
35.            /* 设置变量的当前值和重置值为启动值 */  
36.            *conf->variable = conf->reset_val = newval;  
37.            /* 设置额外数据 */  
38.            conf->gen.extra = conf->reset_extra = extra;  
39.            break;  
40.        }  
41.  
42.        /* 整型、浮点型、字符串型和枚举型GUC变量的初始化与布尔型类似, 
43.           但分别调用对应类型的检查钩子函数,并进行类型检查 */  
44.        // ...(其他类型的GUC变量初始化代码类似,省略以节省空间)  
45.  
46.        case PGC_INT:  
47.        case PGC_REAL:  
48.        case PGC_STRING:  
49.        case PGC_ENUM:  
50.        // ...  
51.    }  
52. }

   接下来InitializeGUCOptions()将每个参数设置为默认值。对于guc_variables中的每个参数,initializeGUCOptions函数先将其config_generic域中的status设置为0,将reset_source、tentative_source、source设置为PGC_ S_DEFAULT 表示默认;stack、sourcefile设置为NULL;然后根据参数值vartype的不同类型分别调用相应的assign_hook函数(如果该参数设置了该函数),assign_hook函数用来设置boot_val,最后将boot_val 赋值给reset_val和variable指向的变量,通过这样一系列的步骤就将参数设置为了默认值。

2.1.3 SetConfigOption

1.void set_config_option(const char *name, const char *value,  
2.                       GucContext context, GucSource source,  
3.                       GucAction action, bool changeVal, int elevel,  
4.                       bool is_reload)  
5.{  
6.    Oid srole; /* 声明用于存储角色ID的变量 */  
7.  
8.    /* 
9.     * 检查配置选项的来源是否为交互式或客户端, 
10.     * 非交互式来源(如配置文件或命令行参数)默认具有所有权限。 
11.     * 特别是,对于数据库角色设置源(PGC_S_GLOBAL等), 
12.     * 我们假设在创建 pg_db_role_setting 条目时已经进行了适当的权限检查。 
13.     */  
14.    if (source >= PGC_S_INTERACTIVE || source == PGC_S_CLIENT)  
15.        srole = GetUserId(); /* 如果是交互式或客户端来源,获取当前用户的ID */  
16.    else  
17.        srole = BOOTSTRAP_SUPERUSERID; /* 否则,使用超级用户的ID,表示具有所有权限 */  
18.  
19.    /* 调用实际执行设置操作的函数,传入所有必要的参数 */  
20.    return set_config_option_ext(name, value,  
21.                                 context, source, srole,  
22.                                 action, changeVal, elevel,  
23.                                 is_reload);  
24.}  

   SetConfigOption 函数是一个简化的包装器,它调用更底层的 set_config_option 函数来实际执行配置的设置。
SetConfigOption 函数接受配置项名称 name 和值 value,以及设置的上下文 context 和来源 source。使用默认参数调用 set_config_option,执行设置操作(GUC_ACTION_SET),允许改变值(changeVal 为 true),没有错误级别(elevel 为 0),不重新加载配置(is_reload 为 false)。
   set_config_option 函数除了接受 SetConfigOption 的参数外,还接受操作类型 action,是否改变值的标志 changeVal,错误级别 elevel,以及是否重新加载配置的标志 is_reload。根据配置来源 source 确定设置权限,如果是交互式或客户端来源,则检查当前用户 ID;否则,默认为超级用户权限。
   调用 set_config_option_ext 函数执行实际的配置设置,传递所有参数。

2.1.4 InitializeGUCOptionsFromEnvironment

1.static void InitializeGUCOptionsFromEnvironment(void)  
2.{  
3.    char *env;  
4.    long stack_rlimit;  
5.  
6.    /* 尝试获取环境变量 "PGPORT",如果存在则设置端口号 */  
7.    env = getenv("PGPORT");  
8.    if (env != NULL)  
9.        SetConfigOption("port", env, PGC_POSTMASTER, PGC_S_ENV_VAR);  
10.  
11.    /* 尝试获取环境变量 "PGDATESTYLE",如果存在则设置日期风格 */  
12.    env = getenv("PGDATESTYLE");  
13.    if (env != NULL)  
14.        SetConfigOption("datestyle", env, PGC_POSTMASTER, PGC_S_ENV_VAR);  
15.  
16.    /* 尝试获取环境变量 "PGCLIENTENCODING",如果存在则设置客户端编码 */  
17.    env = getenv("PGCLIENTENCODING");  
18.    if (env != NULL)  
19.        SetConfigOption("client_encoding", env, PGC_POSTMASTER, PGC_S_ENV_VAR);  
20.  
21.    /* 
22.     * 获取栈深度限制(rlimit),这虽然不是环境变量,但行为类似。 
23.     * 如果可以识别平台的栈深度限制,增加默认栈深度设置到安全值 
24.     * (最多2MB)。如果值来自 rlimit,则报告其来源为 PGC_S_ENV_VAR; 
25.     * 如果设置为2MB,则报告来源为 PGC_S_DYNAMIC_DEFAULT。 
26.     */  
27.    stack_rlimit = get_stack_depth_rlimit();  
28.    if (stack_rlimit > 0)  
29.    {  
30.        long new_limit = (stack_rlimit - STACK_DEPTH_SLOP) / 1024L;  
31.  
32.        if (new_limit > 100)  
33.        {  
34.            GucSource source;  
35.            char limbuf[16];  
36.  
37.            /* 如果新的栈深度限制小于2048KB,使用环境变量来源 */  
38.            if (new_limit < 2048)  
39.                source = PGC_S_ENV_VAR;  
40.            else  
41.            {  
42.                /* 否则,设置为2MB,并使用动态默认值来源 */  
43.                new_limit = 2048;  
44.                source = PGC_S_DYNAMIC_DEFAULT;  
45.            }  
46.            /* 将新的栈深度限制转换为字符串 */  
47.            snprintf(limbuf, sizeof(limbuf), "%ld", new_limit);  
48.            /* 设置 "max_stack_depth" 配置选项 */  
49.            SetConfigOption("max_stack_depth", limbuf,  
50.                            PGC_POSTMASTER, source);  
51.        }  
52.    }  
53.}  

   该函数作用是从环境变量中初始化 He3DB 的 GUC选项。通过系统调用getenv来获得环境变量PGPORT、PGDATESTYLE、PGCLIENTENCODING 的值,不为空则调用SetConfigOption函数来设置这三个变量对应的参数的值。最后,检测系统的最大安全栈深度,如果这个深度值大于100KB且不超过2MB,则用它设置max_stack_depth参数。

2.2 解析命令行

1.while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:S:sTt:W:-:")) != -1)  
2.      {  
3.          switch (opt)  
4.          {  
5.              case 'B':  
6.                  SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);  
7.                  break;      
8.             case 'b':  
9.                  /* Undocumented flag used for binary upgrades */  
10.                  IsBinaryUpgrade = true;  
11.                  break;  
12.    
13.              case 'C':  
14.                 output_config_variable = strdup(optarg);  
15.                  break;  
16.   
17.              case 'D':  
18.                 userDoption = strdup(optarg);  
19.                 break;  

   命令行的解析使用 getopt 函数来实现,它迭代处理所有的命令行参数,并对每个参数执行相应的操作。
getopt 函数每次循环都会尝试解析一个短选项(如 -d)或长选项(如 --debug)。它支持一系列的选项,每个选项都对应一个特定的配置参数或者布尔标志。选项与相应的 case 语句块配对,用于执行特定的设置或操作。例如,如果用户使用 -D 选项指定了一个数据目录,getopt 将解析这个选项,并将解析结果传递给 userDoption 变量。对于大多数配置参数,使用 SetConfigOption 函数来设置其值。
   这个函数接收参数名称、值、上下文(PGC_POSTMASTER 表示是在 postmaster 进程中设置)和来源(PGC_S_ARGV 表示值来自命令行参数)。某些选项,如 ‘b’,用于未记录的内部功能,可能不会调用 SetConfigOption,而是设置一个布尔标志。如果用户使用 ‘C’ 选项请求输出某个配置变量的值,output_config_variable 将被设置,稍后将用于打印该变量的值。如果遇到不支持的选项或参数格式错误,会向用户显示错误消息,并使用 ExitPostmaster 函数以错误状态退出 postmaster 进程。

2.3 读取配置文件

1.if (!SelectConfigFiles(userDoption, progname))  
2.    ExitPostmaster(2);  
1.bool SelectConfigFiles(const char *userDoption, const char *progname)  
2.{  
3.    char *configdir; /* 用于存储配置目录的变量 */  
4.    char *fname; /* 用于存储文件名的变量 */  
5.    struct stat stat_buf; /* 用于存储文件状态的变量 */  
6.  
7.    /* 
8.     * 如果提供了 -D 选项,则使用该选项指定的目录; 
9.     * 否则尝试使用环境变量 $PGDATA 指定的目录。 
10.     */  
11.    if (userDoption)  
12.        configdir = make_absolute_path(userDoption);  
13.    else  
14.        configdir = make_absolute_path(getenv("PGDATA"));  
15.  
16.    /* 如果无法访问配置目录,则报错并返回 false */  
17.    if (configdir && stat(configdir, &stat_buf) != 0)  
18.    {  
19.        write_stderr("%s: could not access directory \"%s\": %s\n",  
20.                     progname, configdir, strerror(errno));  
21.        if (errno == ENOENT)  
22.            write_stderr("Run initdb or pg_basebackup to initialize a PostgreSQL data directory.\n");  
23.        return false;  
24.    }  
25.  
26.    /* 根据命令行参数或配置目录确定配置文件名,并确保使用绝对路径 */  
27.    if (ConfigFileName)  
28.        fname = make_absolute_path(ConfigFileName);  
29.    else if (configdir)  
30.    {  
31.        /* 构造 postgresql.conf 的完整路径 */  
32.        fname = guc_malloc(FATAL, strlen(configdir) + strlen(CONFIG_FILENAME) + 2);  
33.        sprintf(fname, "%s/%s", configdir, CONFIG_FILENAME);  
34.    }  
35.    else  
36.    {  
37.        /* 如果无法确定配置文件位置,则报错并返回 false */  
38.        write_stderr("%s does not know where to find the server configuration file.\n"  
39.                     "You must specify the --config-file or -D invocation "  
40.                     "option or set the PGDATA environment variable.\n",  
41.                     progname);  
42.        return false;  
43.    }  
44.  
45.    /* 设置配置文件名的 GUC 变量,并确保之后不能被覆盖 */  
46.    SetConfigOption("config_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);  
47.    free(fname);  
48.  
49.    /* 首次读取配置文件 */  
50.    if (stat(ConfigFileName, &stat_buf) != 0)  
51.    {  
52.        write_stderr("%s: could not access the server configuration file \"%s\": %s\n",  
53.                     progname, ConfigFileName, strerror(errno));  
54.        free(configdir);  
55.        return false;  
56.    }  
57.  
58.    /* 处理配置文件,仅读取 data_directory 参数 */  
59.    ProcessConfigFile(PGC_POSTMASTER);  
60.  
61.    /* 根据 data_directory GUC 变量或配置目录确定数据目录 */  
62.    if (data_directory)  
63.        SetDataDir(data_directory);  
64.    else if (configdir)  
65.        SetDataDir(configdir);  
66.    else  
67.    {  
68.        write_stderr("%s does not know where to find the database system data.\n",  
69.                     progname);  
70.        return false;  
71.    }  
72.  
73.    /* 将最终的数据目录路径设置回 data_directory GUC 变量 */  
74.    SetConfigOption("data_directory", DataDir, PGC_POSTMASTER, PGC_S_OVERRIDE);  
75.  
76.    /* 第二次读取配置文件,允许 PG_AUTOCONF_FILENAME 文件中的设置生效 */  
77.    ProcessConfigFile(PGC_POSTMASTER);  
78.  
79.    /* 如果配置文件中未设置时区缩写,初始化默认值 */  
80.    pg_timezone_abbrev_initialize();  
81.  
82.    /* 确定 pg_hba.conf 文件的位置并确保使用绝对路径 */  
83.    if (HbaFileName)  
84.        fname = make_absolute_path(HbaFileName);  
85.    else if (configdir)  
86.    {  
87.        /* 构造 pg_hba.conf 的完整路径 */  
88.        fname = guc_malloc(FATAL, strlen(configdir) + strlen(HBA_FILENAME) + 2);  
89.        sprintf(fname, "%s/%s", configdir, HBA_FILENAME);  
90.    }  
91.    else  
92.    {  
93.        write_stderr("%s does not know where to find the \"hba\" configuration file.\n",  
94.                     progname);  
95.        return false;  
96.    }  
97.    SetConfigOption("hba_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);  
98.    free(fname);  
99.  
100.    /* 确定 pg_ident.conf 文件的位置并确保使用绝对路径 */  
101.    // ...(类似处理 pg_ident.conf 文件的代码,省略以节省空间)  
102.  
103.    free(configdir); /* 释放配置目录字符串的内存 */  
104.  
105.    return true; /* 所有步骤成功完成,返回 true */  
106.}  

   为保证配置文件被正确读取并解析,首先,程序使用SelectConfigFiles()函数负责确定配置文件的准确位置。它首先检查命令行参数中是否指定了数据目录(通过-D选项),如果未指定,则尝试从环境变量PGDATA获取。该函数会构建配置文件postgresql.conf的路径,并验证其可访问性。如果配置文件不存在或无法读取,该函数将输出错误信息,可能导致postmaster进程终止。
   紧接着,ProcessConfigFile()函数对已定位的配置文件进行解析。它逐行读取配置文件,识别并应用参数设置。这个函数的调用是分阶段的:首次调用主要是为了获取基础设置,如数据目录路径;第二次调用则用于加载剩余的所有配置选项,包括那些可能依赖于早期设置的选项。
   最后,SetConfigOption()函数在整个启动过程中被用来动态设置全局配置参数(GUC)。该函数接收参数名称、值、上下文和来源,更新相应的GUC变量,并确保更改在整个数据库系统中生效。无论是命令行参数、环境变量还是配置文件中的设置,都通过调用SetConfigOption()来实现参数的最终配置。

三、配置参数添加

添加新的配置参数基本流程

  1. 声明一个类型为布尔型、整型、双精度浮点型或字符指针的全局变量并使用它。
  2. 在guc.c中注册该参数,同时填写该参数一个名称、一个默认值、上下限(如果适用)等,
  3. 如果合适,将其添加到 src/backend/utils/misc/postgresqlsql.conf.sample。
  4. 不要忘记记录该选项(至少在 config.sgml 中)。
  5. 如果它是一个新 GUC_LIST_QUOTE 选项,您必须在 src/bin/pg_dump/dumputils.c 中的variable_is_guc_list_quote() 函数中添加它。

3.1 参数声明

bool类型:
1.extern PGDLLIMPORT bool enable_partition_pruning;  
2.extern PGDLLIMPORT bool enable_async_append;  
Int类型:
1. extern PGDLLIMPORT int min_parallel_table_scan_size;    
2. extern PGDLLIMPORT int min_parallel_index_scan_size;    
Real (float/double)类型:
1.extern PGDLLIMPORT double seq_page_cost;  
2.extern PGDLLIMPORT double random_page_cost;  
String 类型:
1.extern PGDLLIMPORT char *Log_directory;  
2.extern PGDLLIMPORT char *Log_filename;  
enum类型:
1.#ifdef HAVE_SYSLOG  
2.static int  syslog_facility = LOG_LOCAL0;  
3.#else  
4.static int  syslog_facility = 0;  
5.#endif  

3.2 注册参数

3.2.1 conf_bool类型

例子:enable_async_append
在结构体数组static struct config_bool ConfigureNamesBool[] 中注册一个布尔类型配置参数。

1.{  
2.        {"enable_async_append", PGC_USERSET, QUERY_TUNING_METHOD,  
3.            gettext_noop("Enables the planner's use of async append plans."),  
4.            NULL,  
5.            GUC_EXPLAIN  
6.        },  
7.        &enable_async_append,  
8.        true,  
9.        NULL, NULL, NULL  
10.    },  

"enable_async_append":参数名称,表示启用异步追加计划。
PGC_USERSET:表示这个参数是用户可以设置的。
QUERY_TUNING_METHOD:表示这个参数属于查询调整方法的上下文。
gettext_noop("Enables the planner's use of async append plans."):参数的描述,意思是“启用计划器使用异步追加计划”。gettext_noop 是一个宏,用于国际化支持,这里它的作用是提供一个默认语言(通常是英语)的字符串。
NULL:在这个上下文中,NULL 表示没有额外的描述或帮助信息。
变量指针:&enable_async_append 是一个指针,指向存储该参数值的变量。
默认值:true 表示这个参数的默认值是启用状态。
钩子函数:NULL, NULL, NULL 分别是检查设置值、应用设置值和显示帮助信息的钩子函数,这里都设置为 NULL 表示不使用这些钩子。

3.2.2 config_int类型

例子: min_parallel_table_scan_size
在结构体数组static struct config_int ConfigureNamesInt[] 中注册一个int类型配置参数。

1.{  
2.        {"min_parallel_table_scan_size", PGC_USERSET, QUERY_TUNING_COST,  
3.            gettext_noop("Sets the minimum amount of table data for a parallel scan."),  
4.            gettext_noop("If the planner estimates that it will read a number of table pages too small to reach this limit, a parallel scan will not be considered."),  
5.            GUC_UNIT_BLOCKS | GUC_EXPLAIN,  
6.        },  
7.        &min_parallel_table_scan_size,  
8.        (8 * 1024 * 1024) / BLCKSZ, 0, INT_MAX / 3,  
9.        NULL, NULL, NULL  
    },  

参数名称:{“min_parallel_table_scan_size”} 表示最小并行表扫描大小。
分类:PGC_USERSET 表示这个参数是用户可以设置的。
QUERY_TUNING_COST 表示这个参数属于查询调整成本的上下文。
描述
gettext_noop(“Sets the minimum amount of table data for a parallel scan.”) 是参数的描述,意思是“设置并行扫描的表数据的最小量”。
gettext_noop(“If the planner estimates that it will read a number of table pages too small to reach this limit, a parallel scan will not be considered.”) 进一步说明,如果计划器估计读取的表页面数量太小,无法达到这个限制,就不会考虑并行扫描。
标志:GUC_UNIT_BLOCKS | GUC_EXPLAIN 表示这个参数的单位是块(BLOCK),并且可以在 EXPLAIN 命令的输出中显示。
变量:&min_parallel_table_scan_size 是一个指向存储该参数值的变量的指针。
默认值:(8 * 1024 * 1024) / BLCKSZ 定义了该参数的默认值,这里是以数据库块大小为单位的字节数,BLCKSZ 通常是 He3DB中的一个宏,定义了数据库块的大小,默认为 8KB。
最小值:0 表示该参数的最小值。
最大值:INT_MAX / 3 表示该参数的最大值,这里使用了整型最大值除以 3。
钩子函数 :NULL, NULL, NULL 分别是检查设置值、应用设置值和显示帮助信息的钩子函数,这里都设置为 NULL 表示不使用这些钩子。

2.3 config_real类型

例子:seq_page_cost
在结构体数组static struct config_realConfigureNamesReal[]中注册一个real类型配置参数。

1.{  
2.        {"seq_page_cost", PGC_USERSET, QUERY_TUNING_COST,  
3.            gettext_noop("Sets the planner's estimate of the cost of a "  
4.                         "sequentially fetched disk page."),  
5.            NULL,  
6.            GUC_EXPLAIN  
7.        },  
8.        &seq_page_cost,  
9.        DEFAULT_SEQ_PAGE_COST, 0, DBL_MAX,  
10.        NULL, NULL, NULL  
11.    },  

"seq_page_cost":参数名称,表示顺序读取磁盘页的成本估计。
PGC_USERSET:表示这个参数是用户可以设置的。
QUERY_TUNING_COST:表示这个参数属于查询调整成本的上下文。
gettext_noop("Sets the planner's estimate of the cost of a sequentially fetched disk page."):参数的描述,意思是“设置计划器对顺序获取的磁盘页的成本估计”。gettext_noop 用于提供多语言支持的字符串。
NULL:表示没有额外的描述或帮助信息。
变量指针:&seq_page_cost 是一个指针,指向存储该参数值的变量。
默认值:DEFAULT_SEQ_PAGE_COST 是该参数的默认值,通常在 PostgreSQL 的源代码中定义,表示默认情况下计划器对顺序读取磁盘页的成本估计。
最小值:0 表示该参数的最小值,即成本估计不能小于 0。
最大值:DBL_MAX 表示该参数的最大值,DBL_MAX 是 C 语言中的一个宏,表示 double 类型可以表示的最大值。
钩子函数:NULL, NULL, NULL 分别是检查设置值、应用设置值和显示帮助信息的钩子函数,这里都设置为 NULL,表示不使用这些钩子。

3.2.4 config_string类型

例子:Log_directory
在结构体数组static struct config_string ConfigureNamesString[] 中注册一个string类型配置参数。

1.{  
2.        {"log_directory", PGC_SIGHUP, LOGGING_WHERE,  
3.            gettext_noop("Sets the destination directory for log files."),  
4.            gettext_noop("Can be specified as relative to the data directory "  
5.                         "or as absolute path."),  
6.            GUC_SUPERUSER_ONLY  
7.        },  
8.        &Log_directory,  
9.        "log",  
10.        check_canonical_path, NULL, NULL  
11.    },  

"log_directory":参数名称,表示日志文件的目标目录。
PGC_SIGHUP:表示这个参数的更改需要发送 SIGHUP 信号给 PostgreSQL 服务器进程以重新加载配置。
LOGGING_WHERE:表示这个参数属于日志记录位置的上下文。
gettext_noop("Sets the destination directory for log files."):参数的描述,意思是“设置日志文件的目标目录”。
gettext_noop("Can be specified as relative to the data directory or as absolute path."):进一步说明,日志目录可以是相对于数据目录的相对路径,也可以是绝对路径。
GUC_SUPERUSER_ONLY:表示只有超级用户才能更改这个参数。
变量指针:&Log_directory 是一个指针,指向存储该参数值的变量。注意这里应该是 Log_directory 而不是 log_directory,因为变量名通常遵循首字母大写的习惯。
默认值:“log” 表示该参数的默认值,即日志文件默认存放在数据目录下的 log 文件夹中。
检查函数:check_canonical_path 是一个函数,用于在应用这个参数之前检查路径的有效性。这确保了提供的日志目录是一个规范的路径。
应用函数和显示帮助函数:两个 NULL 表示没有指定应用设置值和显示帮助信息的钩子函数。

3.2.5 config_enum类型

例子:syslog_facility
在结构体数组static struct config_enum ConfigureNamesEnum[] 中注册一个enum类型配置参数。\

1.{  
2.          {"syslog_facility", PGC_SIGHUP, LOGGING_WHERE,  
3.              gettext_noop("Sets the syslog \"facility\" to be used when syslog enabled."),  
4.              NULL  
5.         },  
6.          &syslog_facility,  
7.  #ifdef HAVE_SYSLOG  
8.          LOG_LOCAL0,  
9.  #else  
10.          0,  
11.  #endif  
12.          syslog_facility_options,  
13.         NULL, assign_syslog_facility, NULL  
14.      },  

"syslog_facility":参数名称,表示 syslog 设施的设置。
PGC_SIGHUP:表示这个参数的更改需要发送 SIGHUP 信号给 PostgreSQL 服务器进程以重新加载配置。
LOGGING_WHERE:表示这个参数属于日志记录位置的上下文。
gettext_noop("Sets the syslog 'facility' to be used when syslog enabled."):参数的描述,意思是“设置当启用 syslog 时使用的 syslog 设施”。
变量指针:&syslog_facility 是一个指针,指向存储该参数值的变量。使用 #ifdef HAVE_SYSLOG 检查系统是否支持 syslog 功能。如果支持,LOG_LOCAL0 用作默认值,这是一个常见的本地使用设施。如果不支持,使用 0 作为默认值。
选项检查函数:syslog_facility_options 可能是一个函数,用于验证用户输入的 syslog 设施值是否有效。
应用函数:assign_syslog_facility 是一个函数,用于将 syslog 设施的值应用到系统中。这个函数可能负责更新 syslog 配置并重新初始化 syslog 以使用新的设施。
显示帮助函数:最后一个 NULL 表示没有指定显示帮助信息的钩子函数。

3.3 配置文件添加

postgresqlsql.conf.sample中添加所需的新的配置参数相关信息以及其相应的默认值。
bool类型:

1.#enable_async_append = on  
2.#enable_partition_pruning = on  

Int类型:

1.#min_parallel_table_scan_size = 8MB  
2.#min_parallel_index_scan_size = 512kB  

Real (float/double)类型:

1.#seq_page_cost = 1.0            # measured on an arbitrary scale  
2.#random_page_cost = 4.0         # same scale as above  

String 类型:

1.#log_directory = 'log'          # directory where log files are written,  
2.                    # can be absolute or relative to PGDATA  
3.#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'    # log file name pattern,  
3.                    # can include strftime() escapes  

enum类型:

1.#syslog_facility = 'LOCAL0'  

3.4 配置参数记录

config.sgml文件中添加参数的具体描述,通过 config.sgml 文件,可以自动生成postgresqlsqlSQL 的用户手册和配置指南。这些文档详细说明了如何配置和使用数据库的各种参数。
例子:enable_async_append

1.<variablelist>  
2.    <varlistentry id="guc-enable-async-append" xreflabel="enable_async_append">  
3.     <term><varname>enable_async_append</varname> (<type>boolean</type>)  
4.     <indexterm>  
5.      <primary><varname>enable_async_append</varname> configuration parameter</primary>  
6.     </indexterm>  
7.     </term>  
8.     <listitem>  
9.      <para>  
10.       Enables or disables the query planner's use of async-aware  
11.       append plan types. The default is <literal>on</literal>.  
12.      </para>  
13.     </listitem>  
14.    </varlistentry>  

<variablelist>:这个标签定义了一个变量列表,用于列出多个配置参数。
<varlistentry>:这个标签定义了列表中的一个条目,每个条目描述一个特定的配置参数。id=“guc-enable-async-append” 为这个条目提供了一个标识符,xreflabel=“enable_async_append” 可能是用于交叉引用的标签。
<term>:这个标签定义了条目的术语部分,通常包含参数的名称。
<varname>enable_async_append</varname>:表示参数的名称,enable_async_append。
<type>boolean</type>:表示参数的数据类型,这里是布尔型(boolean),意味着这个参数的值可以是 on(真)或 off(假)。
<indexterm>:这个标签用于定义索引项,便于在文档的索引中查找参数。
<primary>:定义了索引的主要术语,这里是参数的名称 enable_async_append。
<listitem>:这个标签定义了条目的描述或详细内容。
<para>:这个标签定义了一个段落,包含对参数的描述。
文本 "Enables or disables the query planner's use of async-aware append plan types."解释了参数的作用:启用或禁用查询计划器使用异步感知的追加计划类型。
"The default is <literal>on</literal>." 说明了参数的默认值是 on,即默认启用状态。

引用文献:

postgresql.conf配置文件详解 :[ https://blog.nowcoder.net/n/29b116f76d5c47449419226d7c275978 ]
PG守护进程(Postmaster)——初始化GUC配置参数 :
[ https://www.cnblogs.com/feishujun/p/PostgreSQLSourceAnalysis_postmaster00.html ]

作者介绍

葛文龙,移动云数据库助理工程师,负责云原生数据库He3DB的研发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值