在创建数据库集簇后,该集簇中默认会包含三个系统数据库template1、template0和postgres,其中template0和postgres都是在初始化过程中从template1复制出来的。这个理论大家想必不是那么陌生,但是template1又是从哪里来的呢?带着这个问题,下文将从代码的角度探究postgres中1号数据库的由来。
initdb代码位于postgres源码下src/bin/initdb/initdb.c中。
subdirs[]
中。static const char *const subdirs[] = {
"global",
"pg_wal/archive_status",
"pg_commit_ts",
"pg_dynshmem",
"pg_notify",
"pg_serial",
"pg_snapshots",
"pg_subtrans",
"pg_twophase",
"pg_multixact",
"pg_multixact/members",
"pg_multixact/offsets",
"base",
"base/1",
"pg_replslot",
"pg_tblspc",
"pg_stat",
"pg_stat_tmp",
"pg_xact",
"pg_logical",
"pg_logical/snapshots",
"pg_logical/mappings"
};
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AaJe9Edk-1653222953327)(4)]
分别通过set_null_conf、test_config_settings、setup_config,设置空的配置文件、测试系统配置、设置配置文件。
测试系统配置: 由大到小测试连接数和共享内存的大小。同时检查系统IPC的类型和时区。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-REcWIswe-1653222953327)(5)]
通过postgres测试模式对系统参数进行检查,如大的参数检查不过依次减小配置进行重新测试,检查命令如下:
# 连接数检测 100
postgres --check -F -c log_checkpoints=false -c max_connections=100 -c shared_buffers=1000 -c dynamic_shared_memory_type=posix < "/dev/null" > "/dev/null" 2>&1
# 共享内存检测 128M
postgres -check -F -c log_checkpoints=false -c max_connections=100 -c shared_buffers=16384 -c dynamic_shared_memory_type=posix < "/dev/null" > "/dev/null" 2>&1
注意: 这个检测只是针对初始化postgres基本的配置参数,实际生产应根据具体服务器的配置进行调整。此处可进行优化,根据检查实际物理内存大小进行自动配合。
设置配置文件:根据检测出的配置,替换模板的配置,模板文件放置在安装目录share
子目录下:
frank@DESKTOP-6NF3B9K:~/pgsql/share$ ll *.sample
-rw-r--r-- 1 frank frank 4703 May 21 22:42 pg_hba.conf.sample
-rw-r--r-- 1 frank frank 1636 May 21 22:42 pg_ident.conf.sample
-rw-r--r-- 1 frank frank 604 May 21 22:42 pg_service.conf.sample
-rw-r--r-- 1 frank frank 29431 May 21 22:42 postgresql.conf.sample
-rw-r--r-- 1 frank frank 278 May 21 22:42 psqlrc.sample
frank@DESKTOP-6NF3B9K:~/pgsql/share$ pwd
/home/frank/pgsql/share
/* Bootstrap template1 */
bootstrap_template1();
// 创建template0
make_template0(cmdfd);
// 创建postgrs
make_postgres(cmdfd);
Linux常用的进程间通信主要有共享内存、信号量、消息队列这几种,广义讲夸主机的进程通信还可以使用socket,但postgres创建template1数据库和进行服务器配置检查使用的是管道的方式。
通道通信的代码:
/*
* macros for running pipes to postgres
*/
#define PG_CMD_DECL char cmd[MAXPGPATH]; FILE *cmdfd
#define PG_CMD_OPEN \
do { \
cmdfd = popen_check(cmd, "w"); \
if (cmdfd == NULL) \
exit(1); /* message already printed by popen_check */ \
} while (0)
#define PG_CMD_CLOSE \
do { \
if (pclose_check(cmdfd)) \
exit(1); /* message already printed by pclose_check */ \
} while (0)
#define PG_CMD_PUTS(line) \
do { \
if (fputs(line, cmdfd) < 0 || fflush(cmdfd) < 0) \
output_failed = true, output_errno = errno; \
} while (0)
#define PG_CMD_PRINTF(fmt, ...) \
do { \
if (fprintf(cmdfd, fmt, __VA_ARGS__) < 0 || fflush(cmdfd) < 0) \
output_failed = true, output_errno = errno; \
} while (0)
步骤如下:
启动命令:postgres --boot -X 16777216 -F -c log_checkpoints=false -d 5
这里先挖个坑吧,后续会详细学习一下bki的使用与执行过程。
bki文件在源码的postgres/src/backend/catalog
目录下,安装完成后在share
目录下。文件名为postgres.bki
。
语法与标准sql类似:
# PostgreSQL 15
create pg_proc 1255 bootstrap rowtype_oid 81
(
oid = oid ,
proname = name ,
......
prosrc = text FORCE NOT NULL ,
probin = text ,
prosqlbody = pg_node_tree ,
proconfig = _text ,
proacl = _aclitem
)
insert ( 1242 boolin 11 10 12 1 0 0 0 f f f t f i s 1 0 16 2275 _null_ _null_ _null_ _null_ _null_ boolin _null_ _null_ _null_ _null_ )
insert ( 1243 boolout 11 10 12 1 0 0 0 f f f t f i s 1 0 2275 16 _null_ _null_ _null_ _null_ _null_ boolout _null_ _null_ _null_ _null_ )
......
创建template1和其中的系统表。
创建了如下的系统表(共64个):
frank@DESKTOP-6NF3B9K:~/pgsql/share$ cat postgres.bki | grep "create pg_"
create pg_proc 1255 bootstrap rowtype_oid 81
create pg_type 1247 bootstrap rowtype_oid 71
create pg_attribute 1249 bootstrap rowtype_oid 75
create pg_class 1259 bootstrap rowtype_oid 83
create pg_attrdef 2604
create pg_constraint 2606
create pg_inherits 2611
create pg_index 2610
create pg_operator 2617
create pg_opfamily 2753
create pg_opclass 2616
create pg_am 2601
create pg_amop 2602
create pg_amproc 2603
create pg_language 2612
create pg_largeobject_metadata 2995
create pg_largeobject 2613
create pg_aggregate 2600
create pg_statistic 2619
create pg_statistic_ext 3381
create pg_statistic_ext_data 3429
create pg_rewrite 2618
create pg_trigger 2620
create pg_event_trigger 3466
create pg_description 2609
create pg_cast 2605
create pg_enum 3501
create pg_namespace 2615
create pg_conversion 2607
create pg_depend 2608
create pg_database 1262 shared_relation rowtype_oid 1248
create pg_db_role_setting 2964 shared_relation
create pg_tablespace 1213 shared_relation
create pg_authid 1260 shared_relation rowtype_oid 2842
create pg_auth_members 1261 shared_relation rowtype_oid 2843
create pg_shdepend 1214 shared_relation
create pg_shdescription 2396 shared_relation
create pg_ts_config 3602
create pg_ts_config_map 3603
create pg_ts_dict 3600
create pg_ts_parser 3601
create pg_ts_template 3764
create pg_extension 3079
create pg_foreign_data_wrapper 2328
create pg_foreign_server 1417
create pg_user_mapping 1418
create pg_foreign_table 3118
create pg_policy 3256
create pg_replication_origin 6000 shared_relation
create pg_default_acl 826
create pg_init_privs 3394
create pg_seclabel 3596
create pg_shseclabel 3592 shared_relation rowtype_oid 4066
create pg_collation 3456
create pg_parameter_acl 6243 shared_relation
create pg_partitioned_table 3350
create pg_range 3541
create pg_transform 3576
create pg_sequence 2224
create pg_publication 6104
create pg_publication_namespace 6237
create pg_publication_rel 6106
create pg_subscription 6100 shared_relation rowtype_oid 6101
create pg_subscription_rel 6102
bootstrap
,那么该表将只在磁盘上创建;不会向pg_class
、pg_attribute
等表里面输入任何与该表相关的东西。因此这样的表将无法被普通的SQL操作访问,直到那些记录被用硬办法(用insert
命令)建立。 这个选项用于创建pg_class
等表本身。shared_relation
,那么表就作为共享表创建。除非声明了without_oids
,否则表将会有OID。表的行类型OID(pg_type
的OID)可以有选择性地通过rowtype_oid
子句指定。如果没有指定,会为之自产生一个OID(如果bootstrap
被指定,则rowtype_oid
是无效的,但不管怎样它还是被写在了文档中)。将template1插入至pg_database
open pg_database
insert ( 1 template1 10 ENCODING LOCALE_PROVIDER t t -1 0 1 1663 LC_COLLATE LC_CTYPE ICU_LOCALE _null_ _null_ )
close pg_database
把1号数据库设为创建数据库的默认模板
create pg_shdescription 2396 shared_relation
(
objoid = oid ,
classoid = oid ,
description = text FORCE NOT NULL
)
open pg_shdescription
insert ( 1 1262 'default template for new databases' )
close pg_shdescription
在pg_namespace中插入3个模式。
create pg_namespace 2615
(
oid = oid ,
nspname = name ,
nspowner = oid ,
nspacl = _aclitem
)
open pg_namespace
insert ( 11 pg_catalog 10 _null_ )
insert ( 99 pg_toast 10 _null_ )
insert ( 2200 public 6171 _null_ )
close pg_namespace
postgres支持的3中pl语言
create pg_language 2612
(
oid = oid ,
lanname = name ,
lanowner = oid ,
lanispl = bool ,
lanpltrusted = bool ,
lanplcallfoid = oid ,
laninline = oid ,
lanvalidator = oid ,
lanacl = _aclitem
)
open pg_language
insert ( 12 internal 10 f f 0 0 2246 _null_ )
insert ( 13 c 10 f f 0 0 2247 _null_ )
insert ( 14 sql 10 f t 0 0 2248 _null_ )
close pg_language
两个默认的表空间
create pg_tablespace 1213 shared_relation
(
oid = oid ,
spcname = name ,
spcowner = oid ,
spcacl = _aclitem ,
spcoptions = _text
)
open pg_tablespace
insert ( 1663 pg_default 10 _null_ _null_ )
insert ( 1664 pg_global 10 _null_ _null_ )
close pg_tablespace
oid | 对象 |
---|---|
1 | template1 |
10 | POSTGRES |
6171 | pg_database_owner |
2200 | public |
11 | pg_catalog |
99 | pg_toast |
1262 | pg_database |
pg_namespace
insert ( 2200 public 6171 _null_ )
根据oid可以看到,public模式在postgres 15中的owner是6171,即pg_database_owner,这是15的新特新。
template0是使用标准SQL进行创建。
"CREATE DATABASE template0 IS_TEMPLATE = true ALLOW_CONNECTIONS = false"
" OID = " CppAsString2(Template0DbOid)
" STRATEGY = file_copy;\n\n",
对template1和template0的datcollversion进行设置
/*
* template0 shouldn't have any collation-dependent objects, so unset
* the collation version. This disables collation version checks when
* making a new database from it.
*/
"UPDATE pg_database SET datcollversion = NULL WHERE datname = 'template0';\n\n",
/*
* While we are here, do set the collation version on template1.
*/
"UPDATE pg_database SET datcollversion = pg_database_collation_actual_version(oid) WHERE datname = 'template1';\n\n",
回收权限
/*
* Explicitly revoke public create-schema and create-temp-table
* privileges in template1 and template0; else the latter would be on
* by default
*/
"REVOKE CREATE,TEMPORARY ON DATABASE template1 FROM public;\n\n",
"REVOKE CREATE,TEMPORARY ON DATABASE template0 FROM public;\n\n",
添加注释
"COMMENT ON DATABASE template0 IS 'unmodifiable empty database';\n\n",
去死皮_
/*
* Finally vacuum to clean up dead rows in pg_database
*/
"VACUUM pg_database;\n\n",
/*
* copy template1 to postgres
*/
static void
make_postgres(FILE *cmdfd)
{
const char *const *line;
/*
* Just as we did for template0, and for the same reasons, assign a fixed
* OID to postgres and select the file_copy strategy.
*/
static const char *const postgres_setup[] = {
"CREATE DATABASE postgres OID = " CppAsString2(PostgresDbOid)
" STRATEGY = file_copy;\n\n",
"COMMENT ON DATABASE postgres IS 'default administrative connection database';\n\n",
NULL
};
for (line = postgres_setup; *line; line++)
PG_CMD_PUTS(*line);
}
可以看到postgres就是对template1的直接复制。
其oid为5
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KTWt60Ft-1653222953328)(images/rWhdF7BY_QHh9K8Sb4WeBR-ZUasYGP25sN_uEs1JQHA.png)]
可以看到在执行完initdb后,base目录下共有3个子目录,分别是1,4,5
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ixTqlkAk-1653222953329)(images/Y0cAvE8gnzbPTCGL_wz3-15zTcSZ6vRZzFa9f2_QGT8.png)]
本文通过源码分析了template1、template0和postgres的创建过程也是initdb进程的运行过程。希望能从底层逻辑来理解postgres的一些特性,更有助于对postgres运行逻辑的深入理解,希望对大家有所帮助。
本期挖的坑,后续填上。
理解bki | 2022-05-22 |
---|