PostgreSQL逻辑备份pg_dump使用及其原理解析

一、原理分析

1、循环调用getopt_long解析命令行参数,将参数保存到static DumpOptions dopt;中
2、判断参数是否相容,不相容则退出:

    options -s/--schema-only and -a/--data-only cannot be used together
    options -c/--clean and -a/--data-only cannot be used together
    options --inserts/--column-inserts and -o/--oids cannot be used together
    option --if-exists requires option -c/--clean

3、调用CreateArchive打开输出文件,输出流为fout。该函数使用4个文件封装了4种不同dump文件格式,增加新文件可以增加新的导出文件类型各自封装,独立易于维护。

    CreateArchive->_allocAH:
        switch (AH->format){
            case archCustom:
                InitArchiveFmt_Custom(AH);
                break;
            case archNull:
                InitArchiveFmt_Null(AH);
                break;
            case archDirectory:
                InitArchiveFmt_Directory(AH);
                break;
            case archTar:
                InitArchiveFmt_Tar(AH);
                break;
            default:
                exit_horribly(modulename, "unrecognized file format \"%d\"\n", fmt);
        }

4、fout是一个重要的全局变量
5、调用ConnectDatabase连接数据库
6、调用setup_connection,在连接上执行一些SQL语句:

    SELECT pg_catalog.set_config(‘search_path‘, ‘‘, false);
    set client_encoding to ‘%s‘//pg_dump -E指定
    SET ROLE %s//
    SET DATESTYLE = ISO;
    SET INTERVALSTYLE = POSTGRES;
    SET extra_float_digits TO 3;
    SET synchronize_seqscans TO off;
    SET statement_timeout = 0;
    SET lock_timeout = 0;
    SET idle_in_transaction_session_timeout = 0;
    SET row_security = off;
    BEGIN;
    SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY;

7、为兼容低版本,根据服务器版本号决定一些变量取值
8、调用tblinfo = getSchemaData(fout, &numTables);决定导出哪些数据库对象。本函数又调用如下函数,值得关注哦。为了存储每个对象的元数据,这些函数会malloc申请空间,直到pg_dump进程结束才释放。

    extinfo = getExtensions(fout, &numExtensions);
    extinfoindex = buildIndexArray(extinfo, numExtensions, sizeof(ExtensionInfo));
    getExtensionMembership(fout, extinfo, numExtensions);
    nspinfo = getNamespaces(fout, &numNamespaces);
    nspinfoindex = buildIndexArray(nspinfo, numNamespaces, sizeof(NamespaceInfo));
    tblinfo = getTables(fout, &numTables);
    tblinfoindex = buildIndexArray(tblinfo, numTables, sizeof(TableInfo));
    getOwnedSeqs(fout, tblinfo, numTables);
    funinfo = getFuncs(fout, &numFuncs);
    funinfoindex = buildIndexArray(funinfo, numFuncs, sizeof(FuncInfo));
    typinfo = getTypes(fout, &numTypes);
    typinfoindex = buildIndexArray(typinfo, numTypes, sizeof(TypeInfo));
    getProcLangs(fout, &numProcLangs);
    getAggregates(fout, &numAggregates);
    oprinfo = getOperators(fout, &numOperators);
    oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
    getAccessMethods(fout, &numAccessMethods);
    getOpclasses(fout, &numOpclasses);
    getOpfamilies(fout, &numOpfamilies);
    getTSParsers(fout, &numTSParsers);
    getTSTemplates(fout, &numTSTemplates);
    getTSDictionaries(fout, &numTSDicts);
    getTSConfigurations(fout, &numTSConfigs);
    getForeignDataWrappers(fout, &numForeignDataWrappers);
    getForeignServers(fout, &numForeignServers);
    getDefaultACLs(fout, &numDefaultACLs);
    collinfo = getCollations(fout, &numCollations);
    collinfoindex = buildIndexArray(collinfo, numCollations, sizeof(CollInfo));
    getConversions(fout, &numConversions);
    getCasts(fout, &numCasts);
    getTransforms(fout, &numTransforms);
    inhinfo = getInherits(fout, &numInherits);
    getEventTriggers(fout, &numEventTriggers);
    processExtensionTables(fout, extinfo, numExtensions);
    flagInhTables(tblinfo, numTables, inhinfo, numInherits);
    getTableAttrs(fout, tblinfo, numTables);
    flagInhAttrs(fout->dopt, tblinfo, numTables);
    getIndexes(fout, tblinfo, numTables);
    getExtendedStatistics(fout);
    getConstraints(fout, tblinfo, numTables);
    getTriggers(fout, tblinfo, numTables);
    getRules(fout, &numRules);
    getPolicies(fout, tblinfo, numTables);
    getPublications(fout);
    getPublicationTables(fout, tblinfo, numTables);
    getSubscriptions(fout);
对于每个getXXXs函数都将执行下面流程,以getTables为例:
1)根据服务器版本号查询系统表,读出对象的元数据信息
2)malloc内存空间并将查询结果存放到对象的数据结构中,TableInfo
3)对于每条元数据信息,调用selectDumpableTable标记需要导出的表,如果-t指定导出表,遍历该列表,得到对应表并标记:DUMP_COMPONENT_ALL;-T指定删除表,标记tbinfo->dobj.dump = DUMP_COMPONENT_NONE
4)dumpIdMap[dobj->dumpId] = dobj;将导出表的元数据存放到dumpIdMap数组中
5)在导出表上执行LOCK TABLE %s IN ACCESS SHARE MODE
6)将所有元数据信息保存后,执行SET statement_timeout = 0保证语句不超时,能够一直执行下去

9、调用getTableData函数,获取表对应的数据。实际上,并不是表真正数据,而是为表数据建立一个“导出对象”,将来导出时,依据导出对象获取真是的数据再导出。虽然先把导出对象放到AH->toc链表上,真正导出时导出数据,不会占用大量内存空间,但是针对这些元数据,当表特别多的时候,由于不到进程退出不释放内存,占用内存还是非常可观的。
该函数调用makeTableDataInfo:
1)view、外部表、分区表字表(从父表导出)和unlogged permanent table不用导出
2)判断该表是否制定导出时被排除
3)malloc一个TableDataInfo,保存表信息

        typedef struct _tableDataInfo
        {
            DumpableObject dobj;
            TableInfo  *tdtable;        /* link to table to dump */
            bool        oids;           /* include OIDs in data? */
            char       *filtercond;     /* WHERE condition to limit rows dumped */
        } TableDataInfo;

4)tdinfo->dobj.catId.tableoid、tdinfo->dobj.catId.oid、tdinfo->dobj.name、tdinfo->dobj.namespace 信息,并将dobj保存到dumpIdMap数组
10、如果需要导出大对虾,调用getBlobs,同上也是保存到数组,并没有真正导出数据
11、调用getDependencies重新整理每个对象的依赖关系。
12、getDumpableObjects从dumpIdMap数组中获取dump对象
13、sortDumpableObjectsByTypeName、sortDataAndIndexObjectsBySize(如果是并行dump,需要按表大小排序)、sortDumpableObjects把所有对象重新排列:不同类型对象导出优先级依赖于dbObjectTypePriority数组;相同类型按名称排序

    static const int dbObjectTypePriority[] =
    {
        1,  /* DO_NAMESPACE */
        4,  /* DO_EXTENSION */
        5,  /* DO_TYPE */
        5,  /* DO_SHELL_TYPE */
        6,  /* DO_FUNC */
        7,  /* DO_AGG */
        8,  /* DO_OPERATOR */
        8,  /* DO_ACCESS_METHOD */
        9,  /* DO_OPCLASS */
        9,  /* DO_OPFAMILY */
        3,  /* DO_COLLATION */
        11, /* DO_CONVERSION */
        18, /* DO_TABLE */
        20, /* DO_ATTRDEF */
        28, /* DO_INDEX */
        29, /* DO_STATSEXT */
        30, /* DO_RULE */
        31, /* DO_TRIGGER */
        27, /* DO_CONSTRAINT */
        32, /* DO_FK_CONSTRAINT */
        2,  /* DO_PROCLANG */
        10, /* DO_CAST */
        23, /* DO_TABLE_DATA */
        24, /* DO_SEQUENCE_SET */
        19, /* DO_DUMMY_TYPE */
        12, /* DO_TSPARSER */
        14, /* DO_TSDICT */
        13, /* DO_TSTEMPLATE */
        15, /* DO_TSCONFIG */
        16, /* DO_FDW */
        17, /* DO_FOREIGN_SERVER */
        32, /* DO_DEFAULT_ACL */
        3,  /* DO_TRANSFORM */
        21, /* DO_BLOB */
        25, /* DO_BLOB_DATA */
        22, /* DO_PRE_DATA_BOUNDARY */
        26, /* DO_POST_DATA_BOUNDARY */
        33, /* DO_EVENT_TRIGGER */
        38, /* DO_REFRESH_MATVIEW */
        34, /* DO_POLICY */
        35, /* DO_PUBLICATION */
        36, /* DO_PUBLICATION_REL */
        37  /* DO_SUBSCRIPTION */
    };

14、dumpEncoding、dumpStdStrings、dumpSearchPath导出编码信息,使用双向链表TOCEntry保存导出对象。例如:

    newToc->defn:"SET client_encoding=‘UTF8‘;\n"
    SET standard_conforming_string=‘on‘;
    SELECT pg_catalog.set_config(‘search_path‘,‘‘,false);\n

15、dumpDatabase导出本链接对应的目的数据库信息,同样是newToc,newToc->defn:CREATE DATABASE yzs WITH TEMPLATE=template0 ENCODING=‘UTF8‘ LC_COLLATE=‘zh_CN.UTF-8‘ LC_CTYPE=‘zh_CN.UTF-8‘
16、遍历所有对象,对于每个对象调用dumpDumpableObject,本函数用一堆诸如dumpNamespace、dumpExtension等,将其插入循环链表。

    for (i = 0; i < numObjs; i++)
        dumpDumpableObject(fout, dobjs[i]);

--------------------------以上所有导出,不真正导出数据----------------------------
17、遍历链表标记哪些对象Toc entry需要导出:ProcessArchiveRestoreOptions
18、如果导出格式时plain,则调用RestoreArchive,输出到文件显示的是SQL语句,不再是不可识别的二进制文件
19、关闭句柄释放资源CloseArchive,根据函数指针调用不同文件类型的_CloseArchive

二、不同格式的处理函数

-F, --format=c|d|t|p output file format (custom, directory, tar,plain text (default))
目前,pg_dump支持4种导出格式:
custum(pg_backup_custum.c):导出二进制格式的文件。包括文件头和文件体。文件体是一个链表,保存每个备份对象,每个可备份对象都有一套统一的结构表示,支持压缩
plain(pg_backup_null.c):把SQL脚本内容输出到标准输出,默认方式
file(pg_backup_file.c):导出包括备份一个主文件和一些辅助文件,主文件方式类似于custom文件格式,辅助文件是数据文件,每个辅助文件对应备份对象中的一个表,需要和-f一起使用
tar(pg_backup_tar.c):文件备份基本类似“file”方式,但最后备份的所有文件都要归档到一个tar文件。文件最大大小为8GB(受限于tar file format)
PostgreSQL通过函数指针来实现这四种导出格式。在pg_backup_archive.h文件中有诸如下面的大量函数指针:

    typedef void (*ClosePtrType) (ArchiveHandle *AH);
    typedef void (*ReopenPtrType) (ArchiveHandle *AH);
    typedef void (*ArchiveEntryPtrType) (ArchiveHandle *AH, TocEntry *te);

这些函数指针,在下面文件里分别初始化:

    pg_backup_custum.c->InitArchiveFmt_Custom(ArchiveHandle *AH)
    pg_backup_null.c->InitArchiveFmt_Null(ArchiveHandle *AH)
    pg_backup_file.c->InitArchiveFmt_Directory(ArchiveHandle *AH)
    pg_backup_tar->InitArchiveFmt_Tar(ArchiveHandle *AH)

在数据结构ArchiveHandle中使用了大量函数指针,是的在初始化不同导出文件格式的Archive结构时,能为处理函数赋值为各自不同的处理函数。这样在pg_dump.c中只需要根据用户指定的文件格式的参数,就可以调用相应的处理函数。见第一部分的第3步。
概况的说,pg_dump导出的内容可以分为数据库对象的定义和数据。数据库对象的定义导出时通过查询系统表把对应元数据信息读取出来后,把该对象的各类信息置于一个链表上包括其依赖对象的oid。而具体的数据,也就是每个数据包的数据也被抽象为一个数据库对象,保存在此链表中。
通过调节导出顺序把数据库对象的定义导出然后导出数据,置于通过链表中对应数据对象节点的信息,执行相应的SQL语句,从表中读出数据然后导出写出去。所以,在内存中只是链表上对象的定义,数据是边读边写出的,可以使用流式读出。

三、使用方法

1)以目录格式导出,需要和-f一起使用。toc.dat保存所有可导出对象的信息(表定义等),其他文件是数据,以表的oid为命名,test是目录。

[postgres@localhost ~]$ pg_dump --format=d yzs -f test
[postgres@localhost ~]$ cd test
[postgres@localhost test]$ ll
total 8
-rw-rw-r--. 1 postgres postgres   31 Mar 23 06:07 3010.dat.gz
-rw-rw-r--. 1 postgres postgres 2124 Mar 23 06:07 toc.dat

2)导出SQL语句到test.sql中

[postgres@localhost ~]$ pg_dump --format=p yzs -f test.sql

3)以二进制格式输出

[postgres@localhost ~]$ pg_dump --format=c -f test yzs

4)以tar格式输出。与d格式不同在于多了一个restore.sql文件(plain格式文件),并将所有文件打包成一个文件

[postgres@localhost ~]$ pg_dump --format=t -f test yzs
[postgres@localhost ~]$ tar -xvf test
toc.dat
3010.dat
restore.sql

5)仅导出数据库结构(不指定库,默认是postgres)

pg_dump -s yzs -f 1.sql

6)导出时导出drop database和create database语句。需注意,导入时如有用户连接这该库,则drop语句执行失败

pg_dump -s yzs -C -c -f 1.txt

7、-t指定导出某些表,只导出item开头的表等对象

pg_dump -t temp* -f 1.txt yzs

8、-n只导出指定的schema,可以多个-n;-N指定不导出的schema

原文地址:https://blog.51cto.com/yanzongshuai/2367950

时间: 2024-10-09 09:53:54

PostgreSQL逻辑备份pg_dump使用及其原理解析的相关文章

PostgreSQL逻辑备份恢复--pg_dump导出及psql导入案例

数据库导入导出是最常用的功能之一.PostgreSQL的备份工具可以使用pg_dump及pg_dumpall.可以通过pg_dump --help获取其使用方法.这里不对其做过多介绍.主要介绍在使用pg_dump及恢复过程中遇到的一个问题. 1.问题使用pg_dump -c导出后,通过psql导入时报下面的错误: ERROR: relation "t1" already exists ERROR: duplicate key value violates unique constrai

PostgreSQL 数据库备份

PostgreSQL 数据库备份 pg_dump 一.备份还原 注意:命令在pg_dump目录下进行 1.备份test数据库 pg_dump -h 127.0.0.1 -p 5432 -U username -c -f db_back.sql test 2.还原数据到test2数据库 psql -U postgres -f /db_back.sql test2 二.命令详解 基本命令 pg_dump [OPTION]... [DBNAME] 注:数据库名放最后,不指定默认是系统变量PGDATAB

ButterKnife 原理解析

一.使用方法 1.添加依赖. implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' 2.使用. public class MainActivity extends AppCompatActivity { // 1.控件id绑定 @BindView(R.id.myBtn) Button myBtn; Unbinder un

推送原理解析 极光推送使用详解

推送原理解析 极光推送使用详解 原军锋 12016.09.22 18:10:07字数 5,705阅读 19,494 推送技术产生场景: --服务器端主动性: 客户端与服务器交互都是客户端主动的, 服务器一般不能主动与客户端进行数据交互, 因为服务器端无法得知客户端的 IP 地址 及 状态; --数据实时性: 如果服务器端有紧急数据要传递给客户端, 就必须主动向客户端发送数据; --基本原理: 使客户端实时获取服务器端消息, Pull 方式, 小周期轮询, 费电费流量; 另一个就是 Push 方式

Spring Boot启动原理解析

Spring Boot启动原理解析http://www.cnblogs.com/moonandstar08/p/6550758.html 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面纱,让它不在神秘. 正文 我们开发任何一个Spring Boot项目,都会用到如下的启动类 从上面代码可以看出,Annotation定义(@Sp

游戏外挂原理解析与制作 - [内存数值修改类 篇一]

本章旨在讲解外挂实现原理,未深入涉及至代码层面.希望能与对这方面感兴趣的朋友多多交流,毕竟理论是死的,套路是固定的,只有破解经验是花大量时间和心血积累的. 对于单机游戏而言,游戏中绝大部分的参数(比如血.蓝.能量亦或是金币)都存储在计算机的堆栈中,一些类似剧情进度的则加密后写入本地的自定义配置文件中: 对于页游.网游和手游,虽然服务器保存了大量的重要的参数,但由于客户端不可避免的需要进行大量的计算和资源的加载,本地内存种必定存有部分的临时变量,通过判断这些变量的变化规律和函数的破密寻到利于自身的

Servlet 工作原理解析

-----转自许令波老师Servlet 工作原理解析  感觉写的很不错,保存下来,留着以后温习 从 Servlet 容器说起 要介绍 Servlet 必须要先把 Servlet 容器说清楚,Servlet 与 Servlet 容器的关系有点像枪和子弹的关系,枪是为子弹而生,而子弹又让枪有了杀伤力.虽然它们是彼此依存的,但是又相互独立发展,这一切都是为了适应工业化生产的结果.从技术角度来说是为了解耦,通过标准化接口来相互协作.既然接口是连接 Servlet 与 Servlet 容器的关键,那我们就

jQuery工作原理解析

jQuery的开篇声明里有一段非常重要的话:jQuery是为了改变javascript的编码方式而设计的. 从这段话可以看出jQuery本身并不是UI组件库或其他的一般AJAX类库. jQuery改变javascript编码方式! 那么它是如何实现它的声明的呢?这里,用以下的一段简短的使用流程: 1)查找(创建)jQuery对象:$(”selector”); 2)调用jQuery对象的方法完成我们需要完成的工作:$(”selector”).doOurWork(); ok,jQuery就是以这种可

开源数据库Postgresql的备份和恢复

最近工作上使用的数据库一直是Postgresql,这是一款开源的数据库,而且任何个人可以将该数据库用于商业用途.在使用Postgresql的时候,让我最明显的感觉就是这数据库做的真心好,虽然说数据库的安装包真的很小,但是性能和操作的便捷是一点也不输给其他商业的大型数据库,另外在命令行界面下对该数据库直接进行操作的感觉真的是很爽.在使用数据库的时候,我们作为小公司的数据库管理员有一项工作是不可能避免的,那就是数据的备份和恢复问题.PostgreSQL虽然各个方面的有点很多,但是在数据库备份这方面,