| Postgresql-数据库参数生效规则 原作者:张升玺 创作时间:2017-03-02 14:13:50+08 |
doudou586 发布于2017-03-03 12:13:50
评论: 1
浏览: 21896
|
欢迎大家踊跃投稿,投稿信箱: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)
通过以上的分析,我们了解到数据库参数的来源、设置过程,已经参数何时可以被修改、修改后何时生效,如何可以查看参数,希望大家能为大家的日常工作带来帮助
