7

走进 PostgreSQL 世界第一篇章--内部结构

 3 years ago
source link: https://blog.zzhpro.com/2020/07/19/postgres-internal-first/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在日常的应用程序开发过程中,都离不开数据库的使用。今天就带大家走进非常有名的关系型数据库管理系统:PostgreSQL。

57YrlKdCIbUeTVH.png

1 安装 PostgreSQL

在深入了解 PostgreSQL 前,先得在我们得主机上安装一下 PostgreSQL。

对于 macOS 用户非常简单,使用以下命令安装

brew install postgresql

一开始我也是这么安装的,它也可以支持升级新的 PostgreSQL,是非常方便的。但是这里我更加推荐一种安装方式,使用 Docker 来安装启动 PostgreSQL,因为它可以跨平台按需使用,更加方便且易于后期修改。
关于 Docker 的安装,可以在 Docker Desktop for Mac and Windows 安装对应的客户端软件。

Docker Desktop
Docker Desktop
docker run --name postgres -p 5432:5432 -v ~/Documents/docker-volumn/postgres:/var/lib/postgresql/data -e POSTGRES_PASSWORD=postgres -d postgres:12-alpine

另外可以去PostgreSQL 官方文档多多了解 PostgreSQL,丰富的知识点在等着你们学习,下图的黄色部分尤其是重点也是难点。

PostgreSQL Document
PostgreSQL Document

2 走进 PostgreSQL 内部

2.1 组织结构

假设我们是使用 macOS 的 brew 安装的 PostgreSQL。那么数据库的文件路径应该在 /usr/local/var/postgres 下。它的目录结构如下所示

# 文件
├── PG_VERSION    主版本号
├── pg_hba.conf
├── pg_ident.conf
├── postgresql.auto.conf
├── postgresql.conf        配置参数
├── postmaster.opts
└── postmaster.pid

# 目录
├── base        数据库文件
├── global
├── pg_commit_ts
├── pg_dynshmem
├── pg_logical
├── pg_multixact        多事务状态数据
├── pg_notify
├── pg_replslot
├── pg_serial
├── pg_snapshots
├── pg_stat
├── pg_stat_tmp
├── pg_subtrans        子事务状态数据
├── pg_tblspc
├── pg_twophase        两阶段事务的状态文件
├── pg_wal        WAL 段文件
└── pg_xact        事务提交状态

数据库中表和索引都是文件形式存储在磁盘上,文件名为 oid,大小最大为 1 GB,超过则会创建一个新的文件。而文件的内部被划分成一个个页,而页的大小默认为 8 KB。

那么怎样才能找到对应的文件在我们磁盘的哪个位置呢?可以利用 SQL 查询得到我们想要的结果。

-- 找到数据库的 oid
select * from pg_database where datname = '数据库名';
-- 找到表或者索引的 oid 或者 relfilenode
select * from pg_class where relname = '表或索引名';

表相关的文件类型有三类,除了存储表数据的文件外,还有后缀为 _fsm 的是空闲空间映射文件,而 _vm 为可见性映射文件。值得注意的是索引文件是没有可见性映射文件的。

➜  16401 pwd
/usr/local/var/postgres/base/16401
➜  16401 ls -la 33657*
# 表相关的文件
-rw-------  1 postgres  postgres  811008 Mar 28 13:02 33657
-rw-------  1 postgres  postgres   24576 Mar 28 13:02 33657_fsm
-rw-------  1 postgres  postgres    8192 Mar 28 13:02 33657_vm

这里跟 MySQL 不同的一点是 MySQL 的页大小为 16 KB,而操作系统默认的页大小也为 4 KB。

2.2 堆表文件

PostgreSQL 内部采用堆元组存储数据本身,因此一个页上将会有多个元组(heap tuple),并使用元组标识符(tuple identifier,tid)来表示,其结构为(页号,行指针偏移量)

  • 头部数据
    • p d_lsn: 最近应用到本页面 XLog 记录的 LSN
    • pd_lower: 指向行指针的末尾,空闲空间开始的位置
    • pd_upper: 指向最新元组的位置,空闲空间结束的位置
  • 行指针
    • 长度为 15 bit
    • 有偏移量,长度和标志这三个属性
  • 堆元组数据
    • HeapTupleHeaderData
      • t_xmin:插入事务的 ID
      • t_xmax:删除或更新的事务的 ID
      • t_cid:命令 ID
      • t_ctid:当前元组或者更新元组的 TID
      • t_infomask2:属性和标志位
      • t_infomask:标志位
      • t_hoff:首部 + 位图 + 填充的长度

2.3 内存结构

  • 本地内存区域
    • work_mem:工作内存,用于排序命令使用
    • maintenance_work_mem:维护工作内存,主要用于自动清理和重新索引
    • temp_buffers:临时缓冲,用于临时表的使用
  • 共享内存区域
    • shared buffer pool:表和索引的数据会加载到这里
    • WAL buffer:存储事务日志
    • commit log:存储事务提交日志
    • 其他共享区域

缓冲区管理器

PostgreSQL 缓冲区管理器由缓冲表、缓冲区描述符和缓冲池组成,管理着共享内存和持久存储之间的数据传输。其维护缓冲区标签(数据库 oid,表 oid,文件类型分支号)到缓冲池页位置的关系,并采用时钟扫描算法(LFU 的一种变体)来进行页面置换,辅以后台的检查点进程和后台写入器进程来进行脏页刷盘。

  • 缓冲表层是一个散列表,存储着页面的缓冲区标签与缓冲区描述符的缓冲 ID(同时也是缓冲池位置) 之间的映射关系,提供共享模式和独占模式的分段式、轻量级锁
  • 缓冲区描述符层是一个由缓冲描述符组成的数组,每个缓冲区描述符与下层的缓冲池槽一一对应,保存着相关页面的元数据。为了加快分配过程,维护了一个空闲列表,用于快速分配下一个可用的缓存区描述符。并提供用于内容读写的共享模式和独占模式的内容锁和独占的 I/O 锁。
  • 缓冲池是一个每个槽为 8 KB,用来保存页面文件数据的一个数组。

2.4 多进程架构

PostgreSQL 采用的多进程的架构,通过以下命令可以找出所有的 postgre 进程。

# 先使用 ps 找到 postgres 的 ppid(上级父程序的 pid)
ps -ef |grep postgres
# 再列出所有的 postgre 的相关进程
➜  ~ pstree -p <ppid>
-+= 00001 root /sbin/launchd
 \-+= 76776 zhihaozhang /usr/local/opt/postgresql/bin/postgres -D /usr/local/va
   |--= 44288 postgres postgres: postgres mydb 127.0.0.1(51293) idle
   |--= 76779 postgres postgres: checkpointer
   |--= 76780 postgres postgres: background writer
   |--= 76781 postgres postgres: walwriter
   |--= 76782 postgres postgres: autovacuum launcher
   |--= 76784 postgres postgres: stats collector
   \--= 76785 postgres postgres: logical replication launcher

PostgreSQL 默认的最大客户端连接数为 100 个,由于多进程的缘故,与 MySQL 的线程连接模型相比,其内存压力更大。

PostgreSQL 的索引有多种类型,其中比较常见的是 B 树,而 MySQL 则使用的是 B+ 树,这是两者的一大区别。

未完待续…


更多精彩内容请关注扫码:

KnowledgeCollision 微信公众号
KnowledgeCollision 微信公众号

Knowledge Collision 激发思维碰撞,IDEA 丛生


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK