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

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
全局流量管理 GTM,标准版 1个月
云原生数据库 PolarDB MySQL 版,通用型 2核8GB 50GB
简介: 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

      相关实践学习
      使用PolarDB和ECS搭建门户网站
      本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
      阿里云数据库产品家族及特性
      阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
      目录
      相关文章
      |
      2月前
      |
      存储 缓存 算法
      HashMap深度解析:从原理到实战
      HashMap,作为Java集合框架中的一个核心组件,以其高效的键值对存储和检索机制,在软件开发中扮演着举足轻重的角色。作为一名资深的AI工程师,深入理解HashMap的原理、历史、业务场景以及实战应用,对于提升数据处理和算法实现的效率至关重要。本文将通过手绘结构图、流程图,结合Java代码示例,全方位解析HashMap,帮助读者从理论到实践全面掌握这一关键技术。
      102 14
      |
      3月前
      |
      存储 关系型数据库 数据库
      【赵渝强老师】PostgreSQL的逻辑存储结构
      PostgreSQL的逻辑存储结构包括数据库集群、数据库、表空间、段、区、块等。每个对象都有唯一的对象标识符OID,并存储于相应的系统目录表中。集群由单个服务器实例管理,包含多个数据库、用户及对象。表空间是数据库的逻辑存储单元,用于组织逻辑相关的数据结构。段是分配给表、索引等逻辑结构的空间集合,区是段的基本组成单位,而块则是最小的逻辑存储单位。
      【赵渝强老师】PostgreSQL的逻辑存储结构
      |
      3月前
      |
      运维 持续交付 云计算
      深入解析云计算中的微服务架构:原理、优势与实践
      深入解析云计算中的微服务架构:原理、优势与实践
      113 3
      |
      4月前
      |
      存储 算法 Java
      解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
      在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
      79 3
      |
      20天前
      |
      机器学习/深度学习 自然语言处理 搜索推荐
      自注意力机制全解析:从原理到计算细节,一文尽览!
      自注意力机制(Self-Attention)最早可追溯至20世纪70年代的神经网络研究,但直到2017年Google Brain团队提出Transformer架构后才广泛应用于深度学习。它通过计算序列内部元素间的相关性,捕捉复杂依赖关系,并支持并行化训练,显著提升了处理长文本和序列数据的能力。相比传统的RNN、LSTM和GRU,自注意力机制在自然语言处理(NLP)、计算机视觉、语音识别及推荐系统等领域展现出卓越性能。其核心步骤包括生成查询(Q)、键(K)和值(V)向量,计算缩放点积注意力得分,应用Softmax归一化,以及加权求和生成输出。自注意力机制提高了模型的表达能力,带来了更精准的服务。
      |
      5天前
      |
      JSON 关系型数据库 PostgreSQL
      PostgreSQL 9种索引的原理和应用场景
      PostgreSQL 支持九种主要索引类型,包括 B-Tree、Hash、GiST、SP-GiST、GIN、BRIN、Bitmap、Partial 和 Unique 索引。每种索引适用于不同场景,如 B-Tree 适合范围查询和排序,Hash 仅用于等值查询,GiST 支持全文搜索和几何数据查询,GIN 适用于多值列和 JSON 数据,BRIN 适合非常大的表,Bitmap 适用于低基数列,Partial 只对部分数据创建索引,Unique 确保列值唯一。
      |
      2月前
      |
      存储 物联网 大数据
      探索阿里云 Flink 物化表:原理、优势与应用场景全解析
      阿里云Flink的物化表是流批一体化平台中的关键特性,支持低延迟实时更新、灵活查询性能、无缝流批处理和高容错性。它广泛应用于电商、物联网和金融等领域,助力企业高效处理实时数据,提升业务决策能力。实践案例表明,物化表显著提高了交易欺诈损失率的控制和信贷审批效率,推动企业在数字化转型中取得竞争优势。
      103 16
      |
      2月前
      |
      网络协议 安全 网络安全
      探索网络模型与协议:从OSI到HTTPs的原理解析
      OSI七层网络模型和TCP/IP四层模型是理解和设计计算机网络的框架。OSI模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,而TCP/IP模型则简化为链路层、网络层、传输层和 HTTPS协议基于HTTP并通过TLS/SSL加密数据,确保安全传输。其连接过程涉及TCP三次握手、SSL证书验证、对称密钥交换等步骤,以保障通信的安全性和完整性。数字信封技术使用非对称加密和数字证书确保数据的机密性和身份认证。 浏览器通过Https访问网站的过程包括输入网址、DNS解析、建立TCP连接、发送HTTPS请求、接收响应、验证证书和解析网页内容等步骤,确保用户与服务器之间的安全通信。
      125 3
      |
      3月前
      |
      JavaScript 前端开发 API
      Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
      Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
      109 17
      |
      3月前
      |
      运维 持续交付 虚拟化
      深入解析Docker容器化技术的核心原理
      深入解析Docker容器化技术的核心原理
      73 1

      推荐镜像

      更多