在postgresql.conf中增加自定义配置项

PostgreSQL提供了postgresql.conf这个配置文件,通过它可以配置PostgreSQL启动、运行、性能优化、主备等各项参数,是PostgreSQL的核心配置文件。笔记简单分析了PostgreSQL是如何创建、应用、变更postgresql.conf的各种配置项,以及如何增加自定义的配置项。
先从PostgreSQL的启动开始说起。老生常谈,PostgreSQL在启动时,首先会启动一个Postmaster主进程,Postmaster主进程的入口函数PostmasterMain在初始化内存环境之后便进入处理数据库各项参数的GUC模块。

一、GUC

GUC名词解释:Grand Unified Configuration
GUC配置模块实现了五种数据类型(bool,int,real,string,enum)的变量配置。这些配置根据生效方式,定义了六种类型:

src/include/utils/guc.h line:67
typedef enum
{
    PGC_INTERNAL,
    PGC_POSTMASTER,
    PGC_SIGHUP,
    PGC_BACKEND,
    PGC_SUSET,
    PGC_USERSET
} GucContext;

  • INTERNAL类型的选项可以通过内部进程,但完全不能由用户设置。这些GUC变量只能通过SHOW显示。
  • POSTMASTER类型的选项只能在postmaster进程启动时,通过读取命令行或配置文件设置被设置。
  • SIGHUP类型的选项只能在postmaster进程启动时被设置,或者当改变了配置文件并发送SIGHUP信号通知postmaster或postgres进程的时候进行配置。
  • BACKEND类型的选项只能在postmaster启动时通过读取配置文件被设置,或由客户端在进行连接时设置。已经启动的后台进程会忽略该类型的选项。
  • SUSET类型的选项只能在postmaster启动时由SUPERUSER通过SQL命令设置。
  • USERSET类型的选项可以由普通用户在任何时候进行设置。

GUC使用config_generic数据结构来定义postgresql.conf的各项参数,例如名称、分组、说明、选项的值的数据类型等等。

config_generic数据结构

src/include/utils/guc_tables.h line:141
struct config_generic
{
    /* constant fields, must be set correctly in initial value: */
    const char *name;           /* name of variable - MUST BE FIRST */
    GucContext  context;        /* context required to set the variable */
    enum config_group group;    /* to help organize variables by function */
    const char *short_desc;     /* short desc. of this variable's purpose */
    const char *long_desc;      /* long desc. of this variable's purpose */
    int         flags;          /* flag bits, see guc.h */
    /* variable fields, initialized at runtime: */
    enum config_type vartype;   /* type of variable (set only at startup) */
    int         status;         /* status bits, see below */
    GucSource   source;         /* source of the current actual value */
    GucSource   reset_source;   /* source of the reset_value */
    GucContext  scontext;       /* context that set the current value */
    GucContext  reset_scontext; /* context that set the reset value */
    GucStack   *stack;          /* stacked prior values */
    void       *extra;          /* "extra" pointer for current actual value */
    char       *sourcefile;     /* file current setting is from (NULL if not
                                 * set in config file) */
    int         sourceline;     /* line in source file */
};

二、初始化配置

在PostmasterMain中,GUC的配置过程如下:

int main(int argc,char *argv[])
{
    /* 初始化GUC选项 */
    InitializeGUCOptions();
    // 选择要使用的postgresql.conf文件和数据目录,并读取postgresql.conf
    SelectConfigFiles(userDoption, progname);
    return 0;
}

两个主要步骤:InitializeGUCOptions()SelectConfigFiles()

InitializeGUCOptions()
初始化所有参数为默认值

SelectConfigFiles()
从命令行参数读取到数据目录之后读取postgresql.conf文件,重新设置GUC的各项参数为postgresql.conf中的配置。

InitializeGUCOptions()的定义

void InitializeGUCOptions(void)
{
    pg_timezone_initialize();
    build_guc_variables();
    for(int i = 0;i<num_guc_variables;i++)
    {
        InitializeOneGUCOptions(guc_variables[i]);
    }
    SetConfigOption("transaction_isolation", "default", PGC_POSTMASTER, PGC_S_OVERRIDE);
    SetConfigOption("transaction_read_only", "no", PGC_POSTMASTER, PGC_S_OVERRIDE);
    SetConfigOption("transaction_deferrable", "no", PGC_POSTMASTER, PGC_S_OVERRIDE);
    InitializeGUCOptionsFromEnvironment();
}

pg_timezone_initialize()

处理时间及pg_log日志的时间;

build_guc_variables()

num_guc_variables变量用来统计参数个数;
guc_variables是实际的config_generic类型的、已排序的、全局唯一的参数数组,通过它查找变量,我认为可以简单的将它理解为一个在内存中的postgresql.conf的映射。它的定义为:static struct config_generic **guc_variables;它是一个config_generic类型的数组。由以下步骤构造guc_variables数组
1) 定义了config_bool,config_int,config_real,config_string,config_enum这几个数据类型,用它们保存不同类型的单个配置项。

// config_bool...的数据结构
src/include/utils/guc_tables.h line:174
struct config_bool
{
    struct config_generic gen;
    /* constant fields, must be set correctly in initial value: */
    bool       *variable;
    bool        boot_val;
    GucBoolCheckHook check_hook;
    GucBoolAssignHook assign_hook;
    GucShowHook show_hook;
    /* variable fields, initialized at runtime: */
    bool        reset_val;
    void       *reset_extra;
};

2) 在guc_tables.h头文件中定义了ConfigureNamesBool,ConfigureNamesInt,ConfigureNamesReal,ConfigureNamesString,ConfigureNamesEnum这几个数组,数组中保存了bool,int,real,string,enum这几种类型的缺省值。

// 例如ConfigureNamesBool:
static struct config_bool ConfigureNamesBool[] =
{
        {
        {"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD,
            gettext_noop("Enables the planner's use of sequential-scan plans."),
            NULL
        },
        &enable_seqscan,
        true,
        NULL, NULL, NULL
    }
};

以上定义了

config_bool.gen.name=“enable_seqscan”
config_bool.gen.context=PGC_USERSET
config_bool.gen.group=QUERY_TUNING_METHOD
config_bool.gen.short_desc=gettext_noop("Enables the planner's use of sequential-scan plans.")
config_bool.gen.long_desc=NULL
config.variable=enable_seqscan
config.boot_val=true
GucBoolCheckHook=NULL
GucBoolAssignHook=NULL
GucShowHook=NULL

3) 接着对上述5个对应不同数据类型的数组进行了两次遍历,第一次遍历得到配置项的总数,设置了配置项的值的数据类型,并为guc_variables分配内存,分配内存时是分配了实际所需内存的1.25倍以方便扩充;第二次遍历从上述5个不同数据类型的数组的gen字段填充guc_variables数组(config_generic类型数组)并根据名称快排。
4) 接下来对上述步骤装载起来的guc_variables遍历,用InitializeOneGUCOptions()逐一设置缺省值,并且通过多个check_hook检查各个配置项是否有效的值。

设置3个重要的transaction的参数为默认值

SetConfigOption("transaction_isolation", "default", PGC_POSTMASTER, PGC_S_OVERRIDE);
SetConfigOption("transaction_read_only", "no", PGC_POSTMASTER, PGC_S_OVERRIDE);
SetConfigOption("transaction_deferrable", "no", PGC_POSTMASTER, PGC_S_OVERRIDE);

InitializeGUCOptionsFromEnvironment()

从环境变量读取PGPORT,PGDATESTYLE,PGCLIENTENCODING的配置。

三、初始化配置之后

在PostmasterMain中
在PostmasterMain中还会根据postmaster进程启动时的参数进行选项的配置,详细的可配置参数信息查看帮助。

$PGHOME/bin/postmaster --help

以一个-B参数为例:

while ((opt = getopt(argc, argv, "A:B")) != -1) 
{
    switch (opt)
    {
        case 'B':                                                                                                                                                                                                                      
        SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);
        break;
    }
}

举例说明

[postgres@l-pgtest1.s.dev.cn6 ~]$ grep shared_buffers /export/pg940_data/postgresql.conf 
shared_buffers = 128MB          # min 128kB
/opt/pg94/bin/postmaster -B 32 -D /export/pg940_data/
[postgres@l-pgtest1.s.dev.cn6 ~]$ psql -p 5444 -c "show shared_buffers"
 shared_buffers 
----------------
 1MB
(1 row)

这里-B参数指定share buffers的page的数量,通过page数量乘以block_size的大小计算shared_buffers大小。

在PostgresMain中
在PostgresMain中完成初始化GUC配置之后,还会将配置报告给客户端。

四、增加一个postgresql.conf配置文件的选项

经过以上理论知识的准备,可以知道增加一个postgresql.conf配置文件的选项只需要两个步骤,在代码guc.c line:662中有简单的说明:

  • 在guc_tables.h头文件中,根据配置项的值的类型,在不同的值类型数组中增加选项
  • 在src/backend/utils/misc/postgresql.conf.sample文件中增加示例。

五、测试

编辑postgresql.conf.sample增加自定义项

vim src/backend/utils/misc/postgresql.conf.sample
#qunar_parameter = 10 #this is a customized options.

编辑代码增加自定义项

vim src/backend/utils/misc/guc.c
{    
     {"qunar_parameter", PGC_SIGHUP, WAL_ARCHIVING,
             gettext_noop("this is a customized options."),
             NULL,
             GUC_UNIT_S
     },   
     &XLogArchiveTimeout,
     0, 0, INT_MAX / 2, 
     NULL, NULL, NULL 
},

编译安装运行

[postgres@l-pgtest1.s.dev.cn6 ~]$ /opt/pgdev/bin/psql -p 5432 postgres
psql (9.4.4)
Type "help" for help.
postgres=# 
postgres=# show qunar_parameter;
 qunar_parameter 
-----------------
 0
(1 row)
postgres=# set qunar_parameter = 10;
ERROR:  parameter "qunar_parameter" cannot be changed now
STATEMENT:  set qunar_parameter = 10;
ERROR:  parameter "qunar_parameter" cannot be changed now
postgres=# \q
[postgres@l-pgtest1.s.dev.cn6 ~]$ vim /export/pgdev_data/postgresql.conf 
[postgres@l-pgtest1.s.dev.cn6 ~]$ /opt/pgdev/bin/pg_ctl -D /export/pgdev_data/ reload
server signaled
LOG:  received SIGHUP, reloading configuration files
[postgres@l-pgtest1.s.dev.cn6 ~]$ LOG:  parameter "qunar_parameter" changed to "10"
[postgres@l-pgtest1.s.dev.cn6 ~]$ /opt/pgdev/bin/psql -p 5432 postgres
psql (9.4.4)
Type "help" for help.
postgres=# show qunar_parameter;
 qunar_parameter 
-----------------
 10s
(1 row)

不修改源码增加自定义的配置,会有什么问题?

[postgres@l-pgtest1.s.dev.cn6 ~]$ vim /export/pgdev_data/postgresql.conf 
[postgres@l-pgtest1.s.dev.cn6 ~]$ /opt/pgdev/bin/pg_ctl -D /export/pgdev_data/ reload
LOG:  received SIGHUP, reloading configuration files
LOG:  unrecognized configuration parameter "qunar_parameter_unchangecode" in file "/export/pgdev_data/postgresql.conf" line 617
LOG:  configuration file "/export/pgdev_data/postgresql.conf" contains errors; no changes were applied
server signaled

六、精简的GUC初始化代码

#include <stdio.h>
#include <sys/types.h>
enum config_type
{
    PGC_BOOL,
    PGC_INT,
    PGC_REAL,
    PGC_STRING,
    PGC_ENUM
}
enum config_group
{
    UNGROUPED,
    FILE_LOCATIONS,
    CONN_AUTH,
    CONN_AUTH_SETTINGS,
    CONN_AUTH_SECURITY,
    RESOURCES,
    RESOURCES_MEM,
    RESOURCES_DISK,
    RESOURCES_KERNEL,
    RESOURCES_VACUUM_DELAY,
    RESOURCES_BGWRITER,
    RESOURCES_ASYNCHRONOUS,
    WAL,
    WAL_SETTINGS,
    WAL_CHECKPOINTS,
    WAL_ARCHIVING,
    REPLICATION,
    REPLICATION_SENDING,
    REPLICATION_MASTER,
    REPLICATION_STANDBY,
    QUERY_TUNING,
    QUERY_TUNING_METHOD,
    QUERY_TUNING_COST,
    QUERY_TUNING_GEQO,
    QUERY_TUNING_OTHER,
    LOGGING,
    LOGGING_WHERE,
    LOGGING_WHEN,
    LOGGING_WHAT,
    STATS,
    STATS_MONITORING,
    STATS_COLLECTOR,
    AUTOVACUUM,
    CLIENT_CONN,
    CLIENT_CONN_STATEMENT,
    CLIENT_CONN_LOCALE,
    CLIENT_CONN_PRELOAD,
    CLIENT_CONN_OTHER,
    LOCK_MANAGEMENT,
    COMPAT_OPTIONS,
    COMPAT_OPTIONS_PREVIOUS,
    COMPAT_OPTIONS_CLIENT,
    ERROR_HANDLING_OPTIONS,
    PRESET_OPTIONS,
    CUSTOM_OPTIONS,
    DEVELOPER_OPTIONS
};
struct config_generic
{
    const char          *name;
    GucContext          context;
    enum config_group   group;
    const char          *short_desc;
    const char          *long_desc;
    int                 flags;
    enum config_type    vartype;
    int                 status;
    GucSource           source;
    GucSource           reset_source;
    GucContext          scontext;
    GucContext          reset_scontext;
    GucStack            *stack;
    void                *extra;
    char                *sourcefile;
    int                 sourceline;
}
typedef enum
{
    PGC_INTERNAL,
    PGC_POSTMASTER,
    PGC_SIGHUP,
    PGC_BACKEND,
    PGC_SUSET,
    PGC_USERSET
} GucContext;
typedef enum
{
    PGC_S_DEFAULT,              /* hard-wired default ("boot_val") */
    PGC_S_DYNAMIC_DEFAULT,      /* default computed during initialization */
    PGC_S_ENV_VAR,              /* postmaster environment variable */
    PGC_S_FILE,                 /* postgresql.conf */
    PGC_S_ARGV,                 /* postmaster command line */
    PGC_S_GLOBAL,               /* global in-database setting */
    PGC_S_DATABASE,             /* per-database setting */
    PGC_S_USER,                 /* per-user setting */
    PGC_S_DATABASE_USER,        /* per-user-and-database setting */
    PGC_S_CLIENT,               /* from client connection request */
    PGC_S_OVERRIDE,             /* special case to forcibly set default */
    PGC_S_INTERACTIVE,          /* dividing line for error reporting */
    PGC_S_TEST,                 /* test per-database or per-user setting */
    PGC_S_SESSION               /* SET command */
} GucSource;
static int num_guc_variables;
static struct config_generic **guc_variables;
static void InitializeOneGUCOptions(struct config_generic * gconf)
{
    //.
}
void SetConfigOption(const char *name, const char *value,
                        GucContext context, GucSource source)
{
    //..
}
void pg_timezone_initialize(void)
{
    printf("initialize timezone.\n");
}
static void InitializeGUCOptionsFromEnvironment(void)
{
    // ...
}
void InitializeGUCOptions(void)
{
    printf("start initialize GUC options.\n");
    pg_timezone_initialize();
    build_guc_variables();
    for(int i = 0;i<num_guc_variables;i++)
    {
        InitializeOneGUCOptions(guc_variables[i]);
    }
    SetConfigOption("transaction_isolation", "default", PGC_POSTMASTER, PGC_S_OVERRIDE);
    SetConfigOption("transaction_read_only", "no", PGC_POSTMASTER, PGC_S_OVERRIDE);
    SetConfigOption("transaction_deferrable", "no", PGC_POSTMASTER, PGC_S_OVERRIDE);
    InitializeGUCOptionsFromEnvironment();
}
void SelectConfigFiles(const char *userDoption, const char *progname)
{
    // ...
}
int main(int argc,char *argv[])
{
    /* options setup */
    InitializeGUCOptions();
    // 选择要使用的postgresql.conf文件和数据目录,并读取postgresql.conf
    SelectConfigFiles(userDoption, progname);
    return 0;
}

七、参考资料

《PostgreSQL数据库内核分析(彭智勇 彭煜玮)》

还没有评论,快来抢沙发!

发表评论

  • 😉
  • 😐
  • 😡
  • 😈
  • 🙂
  • 😯
  • 🙁
  • 🙄
  • 😛
  • 😳
  • 😮
  • emoji-mrgree
  • 😆
  • 💡
  • 😀
  • 👿
  • 😥
  • 😎
  • ➡
  • 😕
  • ❓
  • ❗
  • 66 queries in 0.415 seconds