溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

PostgreSQL邏輯備份pg_dump使用及其原理解析

發(fā)布時間:2020-07-06 17:17:38 來源:網(wǎng)絡(luò) 閱讀:2548 作者:yzs的專欄 欄目:數(shù)據(jù)庫

一、原理分析

1、循環(huán)調(diào)用getopt_long解析命令行參數(shù),將參數(shù)保存到static DumpOptions dopt;中
2、判斷參數(shù)是否相容,不相容則退出:

    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、調(diào)用CreateArchive打開輸出文件,輸出流為fout。該函數(shù)使用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、調(diào)用ConnectDatabase連接數(shù)據(jù)庫
6、調(diào)用setup_connection,在連接上執(zhí)行一些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、為兼容低版本,根據(jù)服務(wù)器版本號決定一些變量取值
8、調(diào)用tblinfo = getSchemaData(fout, &numTables);決定導出哪些數(shù)據(jù)庫對象。本函數(shù)又調(diào)用如下函數(shù),值得關(guān)注哦。為了存儲每個對象的元數(shù)據(jù),這些函數(shù)會malloc申請空間,直到pg_dump進程結(jié)束才釋放。

    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函數(shù)都將執(zhí)行下面流程,以getTables為例:
1)根據(jù)服務(wù)器版本號查詢系統(tǒng)表,讀出對象的元數(shù)據(jù)信息
2)malloc內(nèi)存空間并將查詢結(jié)果存放到對象的數(shù)據(jù)結(jié)構(gòu)中,TableInfo
3)對于每條元數(shù)據(jù)信息,調(diào)用selectDumpableTable標記需要導出的表,如果-t指定導出表,遍歷該列表,得到對應(yīng)表并標記:DUMP_COMPONENT_ALL;-T指定刪除表,標記tbinfo->dobj.dump = DUMP_COMPONENT_NONE
4)dumpIdMap[dobj->dumpId] = dobj;將導出表的元數(shù)據(jù)存放到dumpIdMap數(shù)組中
5)在導出表上執(zhí)行LOCK TABLE %s IN ACCESS SHARE MODE
6)將所有元數(shù)據(jù)信息保存后,執(zhí)行SET statement_timeout = 0保證語句不超時,能夠一直執(zhí)行下去

9、調(diào)用getTableData函數(shù),獲取表對應(yīng)的數(shù)據(jù)。實際上,并不是表真正數(shù)據(jù),而是為表數(shù)據(jù)建立一個“導出對象”,將來導出時,依據(jù)導出對象獲取真是的數(shù)據(jù)再導出。雖然先把導出對象放到AH->toc鏈表上,真正導出時導出數(shù)據(jù),不會占用大量內(nèi)存空間,但是針對這些元數(shù)據(jù),當表特別多的時候,由于不到進程退出不釋放內(nèi)存,占用內(nèi)存還是非常可觀的。
該函數(shù)調(diào)用makeTableDataInfo:
1)view、外部表、分區(qū)表字表(從父表導出)和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數(shù)組
10、如果需要導出大對蝦,調(diào)用getBlobs,同上也是保存到數(shù)組,并沒有真正導出數(shù)據(jù)
11、調(diào)用getDependencies重新整理每個對象的依賴關(guān)系。
12、getDumpableObjects從dumpIdMap數(shù)組中獲取dump對象
13、sortDumpableObjectsByTypeName、sortDataAndIndexObjectsBySize(如果是并行dump,需要按表大小排序)、sortDumpableObjects把所有對象重新排列:不同類型對象導出優(yōu)先級依賴于dbObjectTypePriority數(shù)組;相同類型按名稱排序

    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導出本鏈接對應(yīng)的目的數(shù)據(jù)庫信息,同樣是newToc,newToc->defn:CREATE DATABASE yzs WITH TEMPLATE=template0 ENCODING='UTF8' LC_COLLATE='zh_CN.UTF-8' LC_CTYPE='zh_CN.UTF-8'
16、遍歷所有對象,對于每個對象調(diào)用dumpDumpableObject,本函數(shù)用一堆諸如dumpNamespace、dumpExtension等,將其插入循環(huán)鏈表。

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

--------------------------以上所有導出,不真正導出數(shù)據(jù)----------------------------
17、遍歷鏈表標記哪些對象Toc entry需要導出:ProcessArchiveRestoreOptions
18、如果導出格式時plain,則調(diào)用RestoreArchive,輸出到文件顯示的是SQL語句,不再是不可識別的二進制文件
19、關(guān)閉句柄釋放資源CloseArchive,根據(jù)函數(shù)指針調(diào)用不同文件類型的_CloseArchive

二、不同格式的處理函數(shù)

-F, --format=c|d|t|p output file format (custom, directory, tar,plain text (default))
目前,pg_dump支持4種導出格式:
custum(pg_backup_custum.c):導出二進制格式的文件。包括文件頭和文件體。文件體是一個鏈表,保存每個備份對象,每個可備份對象都有一套統(tǒng)一的結(jié)構(gòu)表示,支持壓縮
plain(pg_backup_null.c):把SQL腳本內(nèi)容輸出到標準輸出,默認方式
file(pg_backup_file.c):導出包括備份一個主文件和一些輔助文件,主文件方式類似于custom文件格式,輔助文件是數(shù)據(jù)文件,每個輔助文件對應(yīng)備份對象中的一個表,需要和-f一起使用
tar(pg_backup_tar.c):文件備份基本類似“file”方式,但最后備份的所有文件都要歸檔到一個tar文件。文件最大大小為8GB(受限于tar file format)
PostgreSQL通過函數(shù)指針來實現(xiàn)這四種導出格式。在pg_backup_archive.h文件中有諸如下面的大量函數(shù)指針:

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

這些函數(shù)指針,在下面文件里分別初始化:

    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)

在數(shù)據(jù)結(jié)構(gòu)ArchiveHandle中使用了大量函數(shù)指針,是的在初始化不同導出文件格式的Archive結(jié)構(gòu)時,能為處理函數(shù)賦值為各自不同的處理函數(shù)。這樣在pg_dump.c中只需要根據(jù)用戶指定的文件格式的參數(shù),就可以調(diào)用相應(yīng)的處理函數(shù)。見第一部分的第3步。
概況的說,pg_dump導出的內(nèi)容可以分為數(shù)據(jù)庫對象的定義和數(shù)據(jù)。數(shù)據(jù)庫對象的定義導出時通過查詢系統(tǒng)表把對應(yīng)元數(shù)據(jù)信息讀取出來后,把該對象的各類信息置于一個鏈表上包括其依賴對象的oid。而具體的數(shù)據(jù),也就是每個數(shù)據(jù)包的數(shù)據(jù)也被抽象為一個數(shù)據(jù)庫對象,保存在此鏈表中。
通過調(diào)節(jié)導出順序把數(shù)據(jù)庫對象的定義導出然后導出數(shù)據(jù),置于通過鏈表中對應(yīng)數(shù)據(jù)對象節(jié)點的信息,執(zhí)行相應(yīng)的SQL語句,從表中讀出數(shù)據(jù)然后導出寫出去。所以,在內(nèi)存中只是鏈表上對象的定義,數(shù)據(jù)是邊讀邊寫出的,可以使用流式讀出。

三、使用方法

1)以目錄格式導出,需要和-f一起使用。toc.dat保存所有可導出對象的信息(表定義等),其他文件是數(shù)據(jù),以表的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)僅導出數(shù)據(jù)庫結(jié)構(gòu)(不指定庫,默認是postgres)

pg_dump -s yzs -f 1.sql

6)導出時導出drop database和create database語句。需注意,導入時如有用戶連接這該庫,則drop語句執(zhí)行失敗

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

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI