postgresql 15源码浅析(1)—— postgres中的1号数据库

摘要

在创建数据库集簇后,该集簇中默认会包含三个系统数据库template1、template0和postgres,其中template0和postgres都是在初始化过程中从template1复制出来的。这个理论大家想必不是那么陌生,但是template1又是从哪里来的呢?带着这个问题,下文将从代码的角度探究postgres中1号数据库的由来。

initdb源码概览

initdb代码位于postgres源码下src/bin/initdb/initdb.c中。

postgresql 15源码浅析(1)—— postgres中的1号数据库_第1张图片
initdb主要功能是创建数据库集簇,包括:

  1. 创建数据库目录,及目录下一些必要的子目录,如base、global、pg_tblspc等,所有要创建的子目录保存在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"
};
  1. 测试当前服务器系统性能,由测试结果创建配置文件postgres.con、pghba.conf、pgident.conf,并对其中定义的参数做一些设置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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
  1. 在bootstrap模式下创建数据库template1,存放在数据目录的子目录base/1下,并通过复制template1来创建template0和postgres两个系统数据库。
/* Bootstrap template1 */
bootstrap_template1();
// 创建template0
make_template0(cmdfd);
// 创建postgrs
make_postgres(cmdfd);

创建template1

进程间统信

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)

步骤如下:

  1. 以bootstrap模式启动postgres进程并打开写管道:

启动命令:postgres --boot -X 16777216 -F -c log_checkpoints=false -d 5

  • –boot,以bootstrap模式启动,这个参数必须作为第一个参数。
  • 关闭fsync
  • -c 配置参数 不开启日志检查点
  • -d 开启debug日志,5是最高级别的debug日志
  • -X 设置wal文件大小,16M
  1. 将bki文件中的命令发送至postgres进程,完成对象的创建。

bki文件

这里先挖个坑吧,后续会详细学习一下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和其中的系统表。

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
  • pg_xxx:表名
  • 后面的 数字为 对象的oid
  • 如果声明了bootstrap,那么该表将只在磁盘上创建;不会向pg_classpg_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

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库

template0是使用标准SQL进行创建。

		"CREATE DATABASE template0 IS_TEMPLATE = true ALLOW_CONNECTIONS = false"
		" OID = " CppAsString2(Template0DbOid)
		" STRATEGY = file_copy;\n\n",
  • IS_TEMPLATE = true:template0是个模板库。
  • ALLOW_CONNECTIONS = false :不允许客户端链接
  • #define Template0DbOid 4 宏定义了template0的oid
  • STRATEGY = file_copy :通过文件copy的方式创建。

对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",

创建postgres库

/*
 * 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

你可能感兴趣的:(postgres,源码浅析,postgresql,postgresql,数据库,源码)