暂无图片
暂无图片
4
暂无图片
暂无图片
暂无图片

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

原创 夏克 2022-05-22
2514

摘要

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

initdb源码概览

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

image.png
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.conf、pg_hba.conf、pg_ident.conf,并对其中定义的参数做一些设置。

image.png

分别通过set_null_conf、test_config_settings、setup_config,设置空的配置文件、测试系统配置、设置配置文件。

测试系统配置: 由大到小测试连接数和共享内存的大小。同时检查系统IPC的类型和时区。
image.png

通过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

image.png

可以看到在执行完initdb后,base目录下共有3个子目录,分别是1,4,5

image.png

结束

本文通过源码分析了template1、template0和postgres的创建过程,也是initdb进程的运行过程。希望能从底层逻辑来理解postgres的一些特性,更有助于对postgres运行逻辑的深入理解,希望对大家有所帮助。

备注

本期挖的坑,后续填上。

待填的坑 挖坑时间
理解bki 2022-05-22
最后修改时间:2022-05-23 09:20:33
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
1人已赞赏
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论