背景
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参数配置过程
参数配置基本过程
:
- 初始化GUC参数:将参数设置为默认值
- 解析postmaster命令行参数:根据postmaster命令行参数进行配置
- 读取参数文件:根据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()来实现参数的最终配置。
三、配置参数添加
添加新的配置参数基本流程
:
- 声明一个类型为布尔型、整型、双精度浮点型或字符指针的全局变量并使用它。
- 在guc.c中注册该参数,同时填写该参数一个名称、一个默认值、上下限(如果适用)等,
- 如果合适,将其添加到 src/backend/utils/misc/postgresqlsql.conf.sample。
- 不要忘记记录该选项(至少在 config.sgml 中)。
- 如果它是一个新 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 是该参数的默认值,通常在 He3DB 的源代码中定义,表示默认情况下计划器对顺序读取磁盘页的成本估计。
最小值
: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 信号给 He3db 服务器进程以重新加载配置。
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 信号给 He3db 服务器进程以重新加载配置。
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 文件,可以自动生成postgresql 的用户手册和配置指南。这些文档详细说明了如何配置和使用数据库的各种参数。
例子:enable_async_append
1.<variablelist>
1. <varlistentry id="guc-enable-async-append" xreflabel="enable_async_append">
2. <term><varname>enable_async_append</varname> (<type>boolean</type>)
3. <indexterm>
4. <primary><varname>enable_async_append</varname> configuration parameter</primary>
5. </indexterm>
6. </term>
7. <listitem>
8. <para>
9. Enables or disables the query planner's use of async-aware
10. append plan types. The default is <literal>on</literal>.
11. </para>
12. </listitem>
13. </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配置文件:详解
PG守护进程(Postmaster)——初始化GUC配置参数 :详解
作者介绍
葛文龙,移动云数据库工程师,负责云原生数据库He3DB的研发。