PG中文社区 /
mdi-home
首页 社区新闻 中文文档 加入ACE {{ item.text }} 登录
mdi-home 首页 mdi-chat-processing 社区新闻 mdi-book-open-variant 中文文档 mdi-account-multiple-check 加入ACE mdi-file-multiple-outline 相关资料 mdi-blank {{item.text}} mdi-exit-to-app 退出账号
Postgresql-数据库参数生效规则

原作者:张升玺  创作时间:2017-03-02 14:13:50+08  
doudou586 发布于2017-03-03 12:13:50           评论: 0   浏览: 18735   顶: 3244  踩: 3357 

Postgresql-数据库参数生效规则

张升玺 北京华宇信息

欢迎大家踊跃投稿,投稿信箱:press@postgres.cn


引言

我们在使用数据库时,是否想过,数据库运行的各种参数,如:端口、最大连接数等参数是从哪里获得的?如果数据库从多个来源都获得了某参数,那么数据库应优先使用哪个值?这些参数修改后何时生效?

参数来源

对于参数来源,我们会整理出的参数来源包括:环境变量、命令行、配置文件、会话修改。 其实参数来源远不只这些,查阅pg的源码backend/utils/misc/guc.c,我们发现来源有14种,如default、environment variable、configuration file、client等,具体如下:

const char *constGucSource_Names[] =
{
        /* PGC_S_DEFAULT */ "default",
        /* PGC_S_DYNAMIC_DEFAULT */ "default",
        /* PGC_S_ENV_VAR */ "environment variable",
        /* PGC_S_FILE */ "configuration file",
        /* PGC_S_ARGV */ "command line",
        /* PGC_S_GLOBAL */ "global",
        /* PGC_S_DATABASE */ "database",
        /* PGC_S_USER */ "user",
        /* PGC_S_DATABASE_USER */ "database user",
        /* PGC_S_CLIENT */ "client",
        /* PGC_S_OVERRIDE */ "override",
        /* PGC_S_INTERACTIVE */ "interactive",
        /* PGC_S_TEST */ "test",
        /* PGC_S_SESSION */ "session"
};

当我们不确定当前起作用的值来自哪里时,我们可以通过查看系统视图pg_settings获得某参数的来源。

select name, setting, source from pg_settings limit 10
             name               |  setting   | source  
---------------------------------+------------+---------
 allow_system_table_mods         | off        | default
 application_name                | psql       | client
 archive_command                 | (disabled) | default
 archive_mode                    | off        | default
 archive_timeout                 | 0          | default
 array_nulls                     | on         | default
 authentication_timeout          | 60         | default
 autovacuum                      | on         | default
 autovacuum_analyze_scale_factor | 0.1        | default
 autovacuum_analyze_threshold    | 50         | default
(10 rows)

参数设置过程

数据库对参数的14种来源,有不同的处理过程,有些是在数据库启动时设置的,有些是在建立数据库连接时设置的,有些可以在连接建立后在会话级别进行设置。

来源为default的参数设置过程

数据库在程序中将涉及到的所有参数根据参数类型的不同用5个数组进行定义:

ConfigureNamesBool(bool型,如fsync)、

ConfigureNamesInt(int型,如shared_buffers)、

ConfigureNamesReal(浮点型变量,如random_page_cost)、

ConfigureNamesString(字符串型,如search_path)、

ConfigureNamesEnum(枚举型变量,如wal_level)。

这些数组定义了参数初始值、及参数合法性检查方法,对于int、real等,还设定了其最大值、最小值的范围。

下面以ConfigureNamesInt为例进行说明其数据结构及参数设置过程:

在backend/utils/misc/guc.c定义了以上5类参数的数组:

static struct config_int ConfigureNamesInt[] = ConfigureNamesInt数组的每个元素为config_int 结构,在include/postgresql/server/utils/guc_tables.h定义了数组的结构,其结构体定义如下:

struct config_int
{
    struct config_generic gen;
    /* constant fields, must be set correctly in initial value: */
    int        *variable;
    int         boot_val;
    int         min;
    int         max;
    GucIntCheckHook check_hook;
    GucIntAssignHook assign_hook;
    GucShowHook show_hook;
    /* variable fields, initialized at runtime: */
    int         reset_val;
    void       *reset_extra;
};

可以看到,结构体包含一些变量:
gen:该变量config_generic 结构体,包括名称、参数何时可以被设置、参数来源等信息。
variable:待赋值的全局变量,该变量是最终在数据库层面生效的变量。
boot_val:启动时默认值(即default值);
min:该参数最小值;
max:该参数可以设置的最大值;
GucIntCheckHook :检查值合法性的函数;

参数数组如下:

static struct config_int ConfigureNamesInt[] =
{
    {
        {"archive_timeout", PGC_SIGHUP, WAL_ARCHIVING,
            gettext_noop("Forces a switch to the next xlog file if a "
                         "new file has not been started within N seconds."),
            NULL,
            GUC_UNIT_S
        },
        &XLogArchiveTimeout,
        0, 0, INT_MAX / 2,
        NULL, NULL, NULL
    },
    ...
}
。。。
static struct config_string ConfigureNamesString[] =
{
    {
        {"archive_command", PGC_SIGHUP, WAL_ARCHIVING,
            gettext_noop("Sets the shell command that will be called to archive a WAL file."),
            NULL
        },
        &XLogArchiveCommand,
        "",
        NULL, NULL, show_archive_command
    },
    {
        {"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE,
            gettext_noop("Sets the client's character set encoding."),
            NULL,
            GUC_IS_NAME | GUC_REPORT
        },
        &client_encoding_string,
        "SQL_ASCII",
        check_client_encoding, assign_client_encoding, NULL
    },
    ...
}   

处理过程为:backend/utils/misc/guc.c通过遍历该数组,将boot_val赋予全局变量(pg的各参数均为全局变量),完成变量初始化的赋值。

static void
InitializeOneGUCOption(struct config_generic * gconf)
{
...
    gconf->source = PGC_S_DEFAULT;
...

    switch (gconf->vartype)
    {
。。。
        case PGC_INT:
            {
                struct config_int *conf = (struct config_int *) gconf;
                int         newval = conf->boot_val;
...
                *conf->variable = conf->reset_val = newval;
...
                break;
            }

在数据库启动时的较早阶段(在其他来源之前),就通过调用nitializeGUCOptions 函数进行初始化,调用堆栈如下:

#0 InitializeOneGUCOption (gconf=) at guc.c:4311

#1 InitializeGUCOptions () at guc.c:4224

#2 PostmasterMain (argc=1, argv=) at postmaster.c:631

#3 main (argc=1, argv=) at main.c:228

该函数运行结束后,数据库的各全局变量就已经有了初始值。

来源为environment variable的参数设置过程

数据库从环境变量中得到4个参数,分别为port、datestyle、client_encoding、max_stack_depth,代码如下。(根据backend/utils/misc/guc.c源码中的注释,从环境变量中获得启动参数,属于兼容老版本而保留的)

void
InitializeGUCOptions(void)
{
    /*
     * For historical reasons, some GUC parameters can receive defaults from
     * environment variables.  Process those settings.
     */
    InitializeGUCOptionsFromEnvironment();
    ...
}   

static void
InitializeGUCOptionsFromEnvironment(void)
{
    char       *env;
    long        stack_rlimit;

    env = getenv("PGPORT");
    if (env != NULL)
        SetConfigOption("port", env, PGC_POSTMASTER, PGC_S_ENV_VAR);

    env = getenv("PGDATESTYLE");
    if (env != NULL)
        SetConfigOption("datestyle", env, PGC_POSTMASTER, PGC_S_ENV_VAR);
    ...
}

如果我们设置PGDATESTYLE为SQL,DMY,重启数据库,再查询当前时间,显示会有变化(由于参数优先级的问题,数据库启动前,需要先把postgresql.conf中的datestyle参数注释掉方可生效)。

export PGDATESTYLE=SQL,DMY
pg_ctl start 
psql postgres sa -c "select now()"
              now               
--------------------------------
 08/02/2017 17:24:33.924814 CST

来源为configuration file的参数设置过程

当我们修改配置文件,如:postgresql.conf文件,重启后,参数会生效。

其处理过程为:数据库启动主函数PostmasterMain ,调用SelectConfigFiles函数对postgresql.conf、postgresql.conf中include的文件、以及postgresql.auto.conf文件进行分析(如postgresql.conf中最大连接数max_connections=2000),解析后max_connections值变为2000。解析后设置参数值堆栈如下:

#0 set_config_option (name=0xe746d8 "max_connections", value=0xe74700 "100", context=PGC_POSTMASTER, source=PGC_S_FILE, action=GUC_ACTION_SET, changeVal=1 '\001', elevel=15, is_reload=0 '\000') at guc.c:6018

#1 0x000000000095cf02 in ProcessConfigFileInternal (context=PGC_POSTMASTER, applySettings=1 '\001', elevel=15) at guc-file.l:440

#2 0x000000000095c86c in ProcessConfigFile (context=PGC_POSTMASTER) at guc-file.l:156

#3 0x0000000000950f54 in SelectConfigFiles (userDoption=0x0, progname=0xe52010 "postgres") at guc.c:4522

#4 0x00000000007694d3 in PostmasterMain (argc=1, argv=0xe529e0) at postmaster.c:828

#5 0x00000000006c19ca in main (argc=1, argv=0xe529e0) at main.c:228

来源为command line的参数设置过程

我们可以通过命令行方式传递参数,如我们可以指定共享内存大小为2GB。

1、普通方式启动

pg_ctl start
psql postgres appuser -c "show shared_buffers"
 shared_buffers 
----------------
 3GB

2、启动时,通过-o参数向postgres传递参数,指定shared_buffers为2GB。

pg_ctl start -o "-c shared_buffers=2GB"
psql postgres appuser -c "show shared_buffers"
 shared_buffers 
----------------
 2GB

处理过程为:数据库启动时,从命令行得到参数,并设置。处理逻辑如下:

while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:W:-:")) != -1)
    {
        switch (opt)
        {
            case 'B':
                SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);
                break;

            ...
            case 'c':
            case '-':
                {
                    char       *name,
                               *value;

                    ...

                    SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV);
                    ...
                }

            ...
        }
    }

来源为global、database、user、database user的参数设置过程

我们可以针对某个全局、某个数据库、某个用户、数据库-用户 设置特定的参数。

如设定某个数据库不可以使用索引扫描:

 ALTER DATABASE postgres SET enable_indexscan TO off;

设置后,重新连到该数据库,查看参数值为off,连到其他库查看参数值为on:

psql postgres sa -c "select name, setting,source from pg_settings where name = 'enable_indexscan'"
       name       | setting |  source  
------------------+---------+----------
 enable_indexscan | off     | database

psql postgres sa -c "select name, setting,source from pg_settings where name = 'enable_indexscan'"
       name       | setting |  source  
------------------+---------+----------
 enable_indexscan | on     | default

处理过程为:在建立连接的初始化过程中,postgres进程加载pg_db_role_setting表中的参数配置,得到配置参数并调用ApplySetting->ProcessGUCArray->set_config_option,对参数进行赋值。

static void
process_settings(Oid databaseid, Oid roleid)
{
    Relation    relsetting;
    Snapshot    snapshot;

    if (!IsUnderPostmaster)
        return;

    relsetting = heap_open(DbRoleSettingRelationId, AccessShareLock);

    /* read all the settings under the same snapsot for efficiency */
    snapshot = RegisterSnapshot(GetCatalogSnapshot(DbRoleSettingRelationId));

    /* Later settings are ignored if set earlier. */
    ApplySetting(snapshot, databaseid, roleid, relsetting, PGC_S_DATABASE_USER);
    ApplySetting(snapshot, InvalidOid, roleid, relsetting, PGC_S_USER);
    ApplySetting(snapshot, databaseid, InvalidOid, relsetting, PGC_S_DATABASE);
    ApplySetting(snapshot, InvalidOid, InvalidOid, relsetting, PGC_S_GLOBAL);

    UnregisterSnapshot(snapshot);
    heap_close(relsetting, AccessShareLock);
}

来源为client的参数设置过程

通过client的命令行或者客户端程序、jdbc驱动默认传入的参数,如:application_name、client_encoding,如jdbc连接串:jdbc:postgres://ip:port/databse;applicationname=abc

调用链为:PostgresMain->InitPostgres->process_startup_options->SetConfigOption

static void
process_startup_options(Port *port, bool am_superuser)
{
    。。。

    /*
     * Process any additional GUC variable settings passed in startup packet.
     * These are handled exactly like command-line variables.
     */
    gucopts = list_head(port->guc_options);
    while (gucopts)
    {
        char       *name;
        char       *value;

        name = lfirst(gucopts);
        gucopts = lnext(gucopts);

        value = lfirst(gucopts);
        gucopts = lnext(gucopts);

        SetConfigOption(name, value, gucctx, PGC_S_CLIENT);
    }
}

来源为override的参数设置过程

该来源是用来避免命令行、参数文件对某些重要参数的配置有误,导致程序运行出现问题而设置的,如:$PGDATA目录、事务日志隔离级别等。程序在得到相应的参数时,即对其进行了设置。如src/backend/utils/misc/guc.c对参数文件路径的设置。

SetConfigOption("config_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);

SetConfigOption("data_directory", DataDir, PGC_POSTMASTER, PGC_S_OVERRIDE);

SetConfigOption("hba_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);

SetConfigOption("ident_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);

来源为interactive的参数设置过程

该模式是通过postgres –single方式,启动单机数据库所用的参数来源,作为特殊用途而保留,可以忽略。

来源为test的参数设置过程

该模式用来对postgres程序进行调试,报告错误时,用到,作为普通用户,可以忽略。

来源为session的参数设置过程

在运行中,我们可以通过set命令修改某些参数,如修改工作内存大小set work_mem=8MB,修改后,该参数来源即为session级:只对当前会话有效。

其处理过程为:函数查询分析器分析得到时set命令,然后交由src/backend/tcop/utility.c的standard_ProcessUtility函数进行处理,处理链为:standard_ProcessUtility->ExecSetVariableStmt->set_config_option。

参数各来源的优先级

通过以上的参数设置过程,发现参数都是通过src/backend/utils/misc/guc.c的set_config_option函数来完成的。 其优先级判断的关键代码为发现某个配置参数的source值大于当前待修改的值的source,则标记changeVal为false,后续判断changeVal为true方进行修改。

因此,参数来源的优先级就是文章最开头constGucSource_Names数组的顺序从低到高排列。

int set_config_option(const char *name, const char *value,
                  GucContext context, GucSource source,
                  GucAction action, bool changeVal, int elevel,
                  bool is_reload)
{
    if (record->source > source)
    {
        ...
        changeVal = false;
    }


    。。。

    if (changeVal)
    {
        /* Save old value to support transaction abort */
        if (!makeDefault)
            push_old_value(&conf->gen, action);

        if (conf->assign_hook)
            (*conf->assign_hook) (newval, newextra);
        *conf->variable = newval;
        set_extra_field(&conf->gen, &conf->gen.extra,
                        newextra);
        conf->gen.source = source;
        conf->gen.scontext = context;
    }

参数修改后何时生效

在数组定义时,通过context属性指定了允许在什么时候修改(即何时生效),如以下int类型参数数组中的PGC_SIGHUP。

static struct config_int ConfigureNamesInt[] =
{
    {
        {"archive_timeout", PGC_SIGHUP, WAL_ARCHIVING,
            gettext_noop("Forces a switch to the next xlog file if a "
                         "new file has not been started within N seconds."),
            NULL,
            GUC_UNIT_S
        },
        &XLogArchiveTimeout,
        0, 0, INT_MAX / 2,
        NULL, NULL, NULL
    },
    ...

context属性的值包括以下几个:

typedef enum
{
    PGC_INTERNAL,
    PGC_POSTMASTER,
    PGC_SIGHUP,
    PGC_SU_BACKEND,
    PGC_BACKEND,
    PGC_SUSET,
    PGC_USERSET
} GucContext;

含义如下:

序号名称含义
1PGC_INTERNAL编译或数据库初始化时即已固定,不可更改
2PGC_POSTMASTER 更改后必须重启方生效,如shared_buffers参数
3PGC_SIGHUP更改后重新加载后即可生效,如autovacuum参数,修改postgresql.conf文件后,可通过pg_ctlreload加载生效
4PGC_SU_BACKEND序号3+超级管理员建立连接时可设置
5PGC_BACKEND序号3+普通用户建立连接可设置
6PGC_SUSET序号4+超级管理员可更改,会话立即生效
7PGC_USERSET序号5+普通用户可更改,会话立即生效

我们通过pg_settings视图的context字段,即可查看某参数何时可以被修改(何时生效):

select name, context from pg_settings limit 10;
              name               |  context   
---------------------------------+------------
 allow_system_table_mods         | postmaster
 application_name                | user
 archive_command                 | sighup
 archive_mode                    | postmaster
 archive_timeout                 | sighup
 array_nulls                     | user
 authentication_timeout          | sighup
 autovacuum                      | sighup
 autovacuum_analyze_scale_factor | sighup
 autovacuum_analyze_threshold    | sighup
(10 rows)

总结

通过以上的分析,我们了解到数据库参数的来源、设置过程,已经参数何时可以被修改、修改后何时生效,如何可以查看参数,希望大家能为大家的日常工作带来帮助

pg_bot_banner.jpg


评论:0   浏览: 18735                   顶: 3244  踩: 3357 

请在登录后发表评论,否则无法保存。


发表评论:
加入我们
QQ群1:5276420
QQ群2:3336901
QQ群3:254622631
文档群:150657323
文档翻译平台:按此访问
社区邮件列表:按此订阅
商业支持
扫码关注
加入我们
QQ群1:5276420
QQ群2:3336901
QQ群3:254622631
文档群:150657323
文档翻译平台:按此访问
社区邮件列表:按此订阅
商业支持
扫码关注
© PostgreSQL中文社区 ... (自2010年起)