PostgreSQL 逻辑备份恢复

逻辑备份是将数据库对象以 SQL 的形式导出,恢复就是将导出的 SQL 灌入数据库再执行一遍以生成一样的数据库对象;而与之相对的是物理备份,它是将数据库对象的物理文件做备份,物理备份恢复的过程需要结合数据库日志利用 PITR 技术恢复到某个状态。

逻辑备份可使用 PostgreSQL 自带的 pg_dump 来实现,pg_dump 支持多种数据库对象的导出形式,比如 SQL 纯文本、PostgreSQL 自定义的二进制格式等;同时也支持指定数据库对象的导出,比如只导出业务表的模式定义,或者导出某个表的测试数据等。逻辑备份的恢复,可以直接使用 PostgreSQL 自带的 pg_restore 来做,当然如果逻辑备份的格式是纯文本的 SQL 形式,使用 psql 直接灌入也是没问题的;使用 pg_restore 来做恢复的话,可选择性就比较多了,可以指定恢复表模式,也可以指定恢复某个表数据。

pg_dump/pg_restore 作为 PostgreSQL 的逻辑备份恢复官方工具,代码位于 src/bin/pg_dump 目录,下面简单介绍一下它们的实现原理。

pg_dump 原理

pg_dump 首先连接到数据库,查询系统表构造出各种不同类型的数据库对象(比如表、视图、索引等)的定义,然后整理出它们之间的依赖关系,查询每一个表构造出它的具体数据,最后将所有内容输出到屏幕或文件;如果要导出的数据库很大,可以指定做并行导出,pg_dump 会以多进程的方式加速导出。

数据一致性

对于导出操作,可能会有一个疑问:如何保证导出数据的一致性呢,尤其是对多进程并行导出来说?pg_dump 主进程连接到数据库后会发起一个只读事务,并且拿到该事务的快照序号(snapshot ID),并共享给所有 worker 进程,这样 pg_dump 所有进程共用一个 snapshot ID 通过 MVCC 保证了数据的一致性。PostgreSQL: Documentation: 13: SET TRANSACTION 底部的示例演示了事务的这种用法,涉及到的相关 SQL 语句如下:

BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 发起只读事务
SELECT pg_export_snapshot(); -- 查询当前事务的 snapshot ID
SET TRANSACTION SNAPSHOT '00000003-0000001B-1'; -- 设置当前事务的 snapshot ID

pg_dump 代码解析

首先创建 Archive 结构记录本次备份,然后创建 ArchiveHandle 结构存储处理备份信息,其次在内存中构建出 DumpableObject 数据并按类型依赖关系排序,并创建它们各自对应的 TOC entry, 在关闭 Archive 时执行数据库对象定义、内容的输出。

Archive 与 ArchiveHandle 结构

CreateArchive() 创建本次逻辑备份的 Archive 结构,该结构是 pg_dump/pg_restore 共用的,用于记录逻辑备份的基本信息,比如参数设置、服务器的版本、并行数量、snapshot ID等,记录服务器版本信息主要是为了兼容不同版本的 PostgreSQL 做备份和恢复。

ArchiveHandle 结构是处理 Archive 结构的 Handle,除了第一个 Archive 结构变量之外,还包括一些处理备份恢复的变量,比如读写备份文件的函数指针、并行处理的函数指针、处理 blob 的函数指针、与 PostgreSQL 服务器建立的链接等;其中 TOC entry 就是挂在这个结构上的。

建立连接

ConnectDatabase() setup_connection() 显而易见,用于建立数据库链接,新建只读事务,存储当前事务的 snapshot ID 等。得到的 PGconn 链接会被存储到 ArchiveHandle 结构中。

dump 内存结构介绍

DumpableObject 结构存储了所有数据库对象公共的最基本的属性,比如 dumpId、name、所属的 namespace、依赖的其他 dumpId 列表等,其中第一个变量 objType 作为枚举类型描述了数据库对象的类型,和 PostgreSQL 一贯的风格一致,这个结构被当作一个基类,所有的数据库对象各自的 dump 结构都基于该结构定义自己的子类,从而实现了不同类型数据库对象的描述和存储。比如TableInfo 是表类型的 dump 结构,它除了基本的 DumpableObject 结构(其中 objType 指示它是表类型)外,还包含了表所拥有的独立的属性,比如 rolname、typrelid 等。

了解了 DumpableObject 和每个数据库对象类型的 dump 结构之后,下面看一下 pg_dump/restore 中如何在内存中存储它们。首先有一个 DumpableObject **dumpIdMap 静态全局 dump 结构指针数组,然后 allocedDumpIds 记录了这个指针数组的最大容量,最后 lastDumpId 记录了指针数组中真实有效的 DumpableObject 结构指针的个数。这个逻辑表现到代码里就是 AssignDumpId() 函数,该函数给传入的 DumpableObject 分配 dumpid 之后将其放入全局变量 dumpIdMap ** 中,lastDumpId 加一;如果 dumpIdMap 空间不够了,那就依靠 allocedDumpIds 扩充其空间。

getSchemaData

getSchemaData() 将索引、约束、函数、类型等数据库对象从系统表中查询出来,构造成 DumpableObject 结构,调用 AssignDumpId() 放到全局变量 dumpIdMap ** 中。比如 getTables() 用来 dump 表数据,后面 getTableData() 构造表数据对应的 DumpableObject ,放入 dumpIdMap 中。

getExtensions()
getNamespaces()
getTables()
getFuncs()
getTypes()
getProcLangs()
getAggregates()
getOperators()
getAccessMethods()
getOpclasses()
getOpfamilies()
getTSParsers()
getTSTemplates()
getTSDictionaries()
getTSConfigurations()
getForeignDataWrappers()
getForeignServers()
getDefaultACLs()
getCollations()
getConversions()
getCasts()
getTransforms()
getInherits()
getEventTriggers()
getTableAttrs()
getIndexes()
getExtendedStatistics()
getConstraints()
getTriggers()
getRules()
getPolicies()
getPublications()
getPublicationTables()
getSubscriptions()

排序、依赖

创建 boundary dump object,循环 dumIdMap,在pre-data,post-data时插入 boundary dump object,以区别边界;按照 namespace/schema 排序 dumIdMap,再按照依赖排序,避免依赖乱序出问题。

TOC entry

构造 achive TOC entry,每个 DumpableObject 代表了一个数据库对象,dumpDumpableObject() 函数遍历每一个对象,为其生成对应的 TOC entry,比如 index 的 DumpableObject ,对应的 TOC entry 里面就有了 createstmt、dropstmt 等。

至此,在内存中已经存放好了 dumpobj,以及它们的 TOC entry。

处理输出

ProcessArchiveRestoreOptions() 遍历 TOC entry,依据参数,做决定(_tocEntryRequired())是否要输出该 entry,存储到 te->reqs 变量中。BuildArchiveDependencies() 处理 TOC entry 的依赖,表现在 te->nDeps、te->dependencies 变量上。

ahprintf

ahprintf() 函数输出格式化的SQL语句,按照参数的不同,可能 stdout,可能到文件,也可能是直接连到数据库上执行,是一个 pg_dump/restore 共用的函数。

如果不是直接输出到屏幕,那就会 AH->ClosePtr(AH),依照函数指针的不同,可能写到文件或者目录。

_CloseArchive() 这是开始写文件的函数,pg_dump 在这之前都是在内存里的,它的逻辑如下
– WriteHead() 写 PGDMP 标志头
– WriteToc() 按照 te->reqs 要求执行是否写入该 TOC entry
– WriteDataChunks() 写表具体的数据

pg_restore 代码解析

待续

参考链接

PostgreSQL: Documentation: 13: pg_dump
PostgreSQL: Documentation: 13: pg_restore
pg_dump
pg_restore
PgSQL · 源码分析· pg_dump分析

分类:PostgreSQL

Tagged as: , ,

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

您正在使用您的 WordPress.com 账号评论。 注销 /  更改 )

Google photo

您正在使用您的 Google 账号评论。 注销 /  更改 )

Twitter picture

您正在使用您的 Twitter 账号评论。 注销 /  更改 )

Facebook photo

您正在使用您的 Facebook 账号评论。 注销 /  更改 )

Connecting to %s

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理