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

yzs87發表於2019-03-23

一、原理分析

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


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31493717/viewspace-2639189/,如需轉載,請註明出處,否則將追究法律責任。

相關文章