Postgresql-数据库参数生效规则 原作者:张升玺 创作时间:2017-03-02 14:13:50+08 |
doudou586 发布于2017-03-03 12:13:50 评论: 1 浏览: 19966 顶: 3617 踩: 3697 |
欢迎大家踊跃投稿,投稿信箱: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
该函数运行结束后,数据库的各全局变量就已经有了初始值。
数据库从环境变量中得到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
当我们修改配置文件,如: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
我们可以通过命令行方式传递参数,如我们可以指定共享内存大小为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); ... } ... } }
我们可以针对某个全局、某个数据库、某个用户、数据库-用户 设置特定的参数。
如设定某个数据库不可以使用索引扫描:
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的命令行或者客户端程序、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); } }
该来源是用来避免命令行、参数文件对某些重要参数的配置有误,导致程序运行出现问题而设置的,如:$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);
该模式是通过postgres –single方式,启动单机数据库所用的参数来源,作为特殊用途而保留,可以忽略。
该模式用来对postgres程序进行调试,报告错误时,用到,作为普通用户,可以忽略。
在运行中,我们可以通过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;
含义如下:
序号 | 名称 | 含义 |
---|---|---|
1 | PGC_INTERNAL | 编译或数据库初始化时即已固定,不可更改 |
2 | PGC_POSTMASTER | 更改后必须重启方生效,如shared_buffers参数 |
3 | PGC_SIGHUP | 更改后重新加载后即可生效,如autovacuum参数,修改postgresql.conf文件后,可通过pg_ctlreload加载生效 |
4 | PGC_SU_BACKEND | 序号3+超级管理员建立连接时可设置 |
5 | PGC_BACKEND | 序号3+普通用户建立连接可设置 |
6 | PGC_SUSET | 序号4+超级管理员可更改,会话立即生效 |
7 | PGC_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)
通过以上的分析,我们了解到数据库参数的来源、设置过程,已经参数何时可以被修改、修改后何时生效,如何可以查看参数,希望大家能为大家的日常工作带来帮助