本节简单介绍了PostgreSQL的备份工具pg_basebackup源码中实际执行备份逻辑的函数BaseBackup.
一、数据结构
option
使用工具时存储选项的数据结构
#ifndef HAVE_STRUCT_OPTION
//工具软件选项
struct option
{
const char *name;//名称
int has_arg;//是否包含参数,no_argument/required_argument/optional_argument
int *flag;//标记
int val;//参数值
};
#define no_argument 0
#define required_argument 1
#define optional_argument 2
#endif
#ifndef HAVE_INT_OPTERR
int opterr = 1,
optind = 1,
optopt;
char *optarg;
#endif
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
pg_result
用于接收PQgetResult的返回结果.
struct pg_result
{
//元组数量
int ntups;
//属性数量
int numAttributes;
PGresAttDesc *attDescs;
//PGresTuple数组
PGresAttValue **tuples;
//元组数组的大小
int tupArrSize;
//参数格式
int numParameters;
//参数描述符
PGresParamDesc *paramDescs;
//执行状态类型(枚举变量)
ExecStatusType resultStatus;
//从查询返回的命令状态
char cmdStatus[CMDSTATUS_LEN];
//1-二进制的元组数据,否则为文本数据
int binary;
//钩子函数
PGNoticeHooks noticeHooks;
PGEvent *events;
int nEvents;
int client_encoding;
//错误信息
char *errMsg;
//按字段拆分的信息
PGMessageField *errFields;
//如可用,触发查询的文本信息
char *errQuery;
//查询结果中的所有NULL属性指向该null字符串
char null_field[1];
//最近已分配的block
PGresult_data *curBlock;
//块中空闲空间的开始偏移
int curOffset;
//块中剩余的空闲字节
int spaceLeft;
//该PGresult结构体总共的分配空间
size_t memorySize;
};
//prepared statement语句的单个参数的数据
typedef struct pgresParamDesc
{
//类型ID
Oid typid;
} PGresParamDesc;
typedef enum
{
//空查询串
PGRES_EMPTY_QUERY = 0,
//后台进程正常执行了没有结果返回的查询命令
PGRES_COMMAND_OK,
//后台进程正常执行了有元组返回的查询命令
//PGresult中有结果元组
PGRES_TUPLES_OK,
//拷贝数据OUT,传输中
PGRES_COPY_OUT,
//拷贝数据IN,传输中
PGRES_COPY_IN,
//从后台进程中收到非期望中的响应
PGRES_BAD_RESPONSE,
//提示或警告信息
PGRES_NONFATAL_ERROR,
//查询失败
PGRES_FATAL_ERROR,
//拷贝I/O,传输中
PGRES_COPY_BOTH,
//更大的结果集中的单个元组
PGRES_SINGLE_TUPLE
} ExecStatusType;
typedef union pgresult_data PGresult_data;
union pgresult_data
{
//链接到下一个block,或者为NULL
PGresult_data *next;
//以字节形式访问块
char space[1];
};
二、源码解读
BaseBackup,实际执行备份的函数.
主要逻辑是通过libpq接口向服务器端发起备份请求(BASE_BACKUP命令)
static void
BaseBackup(void)
{
PGresult *res;
char *sysidentifier;
TimeLineID latesttli;
TimeLineID starttli;
char *basebkp;
char escaped_label[MAXPGPATH];
char *maxrate_clause = NULL;
int i;
char xlogstart[64];
char xlogend[64];
int minServerMajor,
maxServerMajor;
int serverVersion,
serverMajor;
//数据库连接
Assert(conn != NULL);
minServerMajor = 901;
maxServerMajor = PG_VERSION_NUM / 100;
serverVersion = PQserverVersion(conn);
serverMajor = serverVersion / 100;
if (serverMajor < minServerMajor || serverMajor > maxServerMajor)
{
const char *serverver = PQparameterStatus(conn, "server_version");
fprintf(stderr, _("%s: incompatible server version %s\n"),
progname, serverver ? serverver : "'unknown'");
exit(1);
}
if (includewal == STREAM_WAL && !CheckServerVersionForStreaming(conn))
{
fprintf(stderr, _("HINT: use -X none or -X fetch to disable log streaming\n"));
exit(1);
}
if (writerecoveryconf)
GenerateRecoveryConf(conn);
if (!RunIdentifySystem(conn, &sysidentifier, &latesttli, NULL, NULL))
exit(1);
PQescapeStringConn(conn, escaped_label, label, sizeof(escaped_label), &i);
if (maxrate > 0)
maxrate_clause = psprintf("MAX_RATE %u", maxrate);
if (verbose)
//提示信息
fprintf(stderr,
_("%s: initiating base backup, waiting for checkpoint to complete\n"),
progname);
if (showprogress && !verbose)
{
//进度信息
fprintf(stderr, "waiting for checkpoint");
if (isatty(fileno(stderr)))
fprintf(stderr, "\r");
else
fprintf(stderr, "\n");
}
//base backup命令
basebkp =
psprintf("BASE_BACKUP LABEL '%s' %s %s %s %s %s %s %s",
escaped_label,
showprogress ? "PROGRESS" : "",
includewal == FETCH_WAL ? "WAL" : "",
fastcheckpoint ? "FAST" : "",
includewal == NO_WAL ? "" : "NOWAIT",
maxrate_clause ? maxrate_clause : "",
format == 't' ? "TABLESPACE_MAP" : "",
verify_checksums ? "" : "NOVERIFY_CHECKSUMS");
//调用API
if (PQsendQuery(conn, basebkp) == 0)
{
fprintf(stderr, _("%s: could not send replication command \"%s\": %s"),
progname, "BASE_BACKUP", PQerrorMessage(conn));
exit(1);
}
//获取PQ执行结果
res = PQgetResult(conn);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, _("%s: could not initiate base backup: %s"),
progname, PQerrorMessage(conn));
exit(1);
}
//判断ntuples
if (PQntuples(res) != 1)
{
fprintf(stderr,
_("%s: server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected %d rows and %d fields\n"),
progname, PQntuples(res), PQnfields(res), 1, 2);
exit(1);
}
//获取WAL start位置
strlcpy(xlogstart, PQgetvalue(res, 0, 0), sizeof(xlogstart));
if (verbose)
fprintf(stderr, _("%s: checkpoint completed\n"), progname);
if (PQnfields(res) >= 2)
starttli = atoi(PQgetvalue(res, 0, 1));
else
starttli = latesttli;
PQclear(res);
MemSet(xlogend, 0, sizeof(xlogend));
if (verbose && includewal != NO_WAL)
fprintf(stderr, _("%s: write-ahead log start point: %s on timeline %u\n"),
progname, xlogstart, starttli);
res = PQgetResult(conn);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, _("%s: could not get backup header: %s"),
progname, PQerrorMessage(conn));
exit(1);
}
if (PQntuples(res) < 1)
{
fprintf(stderr, _("%s: no data returned from server\n"), progname);
exit(1);
}
totalsize = totaldone = 0;
tablespacecount = PQntuples(res);
for (i = 0; i < PQntuples(res); i++)
{
totalsize += atol(PQgetvalue(res, i, 2));
if (format == 'p' && !PQgetisnull(res, i, 1))
{
char *path = unconstify(char *, get_tablespace_mapping(PQgetvalue(res, i, 1)));
verify_dir_is_empty_or_create(path, &made_tablespace_dirs, &found_tablespace_dirs);
}
}
if (format == 't' && strcmp(basedir, "-") == 0 && PQntuples(res) > 1)
{
fprintf(stderr,
_("%s: can only write single tablespace to stdout, database has %d\n"),
progname, PQntuples(res));
exit(1);
}
if (includewal == STREAM_WAL)
{
if (verbose)
fprintf(stderr, _("%s: starting background WAL receiver\n"),
progname);
StartLogStreamer(xlogstart, starttli, sysidentifier);
}
for (i = 0; i < PQntuples(res); i++)//所有的表空间
{
if (format == 't')
//tar包
ReceiveTarFile(conn, res, i);
else
//普通文件
ReceiveAndUnpackTarFile(conn, res, i);
}
if (showprogress)
{
progress_report(PQntuples(res), NULL, true);
if (isatty(fileno(stderr)))
fprintf(stderr, "\n");
}
PQclear(res);
res = PQgetResult(conn);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr,
_("%s: could not get write-ahead log end position from server: %s"),
progname, PQerrorMessage(conn));
exit(1);
}
if (PQntuples(res) != 1)
{
fprintf(stderr,
_("%s: no write-ahead log end position returned from server\n"),
progname);
exit(1);
}
strlcpy(xlogend, PQgetvalue(res, 0, 0), sizeof(xlogend));
if (verbose && includewal != NO_WAL)
fprintf(stderr, _("%s: write-ahead log end point: %s\n"), progname, xlogend);
PQclear(res);
//
res = PQgetResult(conn);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
const char *sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
if (sqlstate &&
strcmp(sqlstate, ERRCODE_DATA_CORRUPTED) == 0)
{
fprintf(stderr, _("%s: checksum error occurred\n"),
progname);
checksum_failure = true;
}
else
{
fprintf(stderr, _("%s: final receive failed: %s"),
progname, PQerrorMessage(conn));
}
exit(1);
}
if (bgchild > 0)
{
#ifndef WIN32
int status;
pid_t r;
#else
DWORD status;
intptr_t bgchild_handle = bgchild;
uint32 hi,
lo;
#endif
if (verbose)
fprintf(stderr,
_("%s: waiting for background process to finish streaming ...\n"), progname);
#ifndef WIN32//WIN32
if (write(bgpipe[1], xlogend, strlen(xlogend)) != strlen(xlogend))
{
fprintf(stderr,
_("%s: could not send command to background pipe: %s\n"),
progname, strerror(errno));
exit(1);
}
r = waitpid(bgchild, &status, 0);
if (r == (pid_t) -1)
{
fprintf(stderr, _("%s: could not wait for child process: %s\n"),
progname, strerror(errno));
exit(1);
}
if (r != bgchild)
{
fprintf(stderr, _("%s: child %d died, expected %d\n"),
progname, (int) r, (int) bgchild);
exit(1);
}
if (status != 0)
{
fprintf(stderr, "%s: %s\n",
progname, wait_result_to_str(status));
exit(1);
}
#else
if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
{
fprintf(stderr,
_("%s: could not parse write-ahead log location \"%s\"\n"),
progname, xlogend);
exit(1);
}
xlogendptr = ((uint64) hi) << 32 | lo;
InterlockedIncrement(&has_xlogendptr);
if (WaitForSingleObjectEx((HANDLE) bgchild_handle, INFINITE, FALSE) !=
WAIT_OBJECT_0)
{
_dosmaperr(GetLastError());
fprintf(stderr, _("%s: could not wait for child thread: %s\n"),
progname, strerror(errno));
exit(1);
}
if (GetExitCodeThread((HANDLE) bgchild_handle, &status) == 0)
{
_dosmaperr(GetLastError());
fprintf(stderr, _("%s: could not get child thread exit status: %s\n"),
progname, strerror(errno));
exit(1);
}
if (status != 0)
{
fprintf(stderr, _("%s: child thread exited with error %u\n"),
progname, (unsigned int) status);
exit(1);
}
#endif
}
//释放配置文件内存
destroyPQExpBuffer(recoveryconfcontents);
PQclear(res);
PQfinish(conn);
conn = NULL;
if (do_sync)
{
if (verbose)
fprintf(stderr,
_("%s: syncing data to disk ...\n"), progname);
if (format == 't')
{
if (strcmp(basedir, "-") != 0)
(void) fsync_fname(basedir, true, progname);
}
else
{
(void) fsync_pgdata(basedir, progname, serverVersion);
}
}
if (verbose)
fprintf(stderr, _("%s: base backup completed\n"), progname);
}
char *
PQgetvalue(const PGresult *res, int tup_num, int field_num)
{
if (!check_tuple_field_number(res, tup_num, field_num))
return NULL;
return res->tuples[tup_num][field_num].value;
}
三、跟踪分析
备份命令
pg_basebackup -h localhost -U xdb -p 5432 -D /data/backup -P -Xs -R
启动gdb跟踪
[xdb@localhost ~]$ gdb pg_basebackup
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-110.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /appdb/atlasdb/pg11.2/bin/pg_basebackup...done.
(gdb) b BaseBackup
(gdb) set args -h localhost -U xdb -p 5432 -D /data/backup -P -Xs -R
(gdb) r
Starting program: /appdb/atlasdb/pg11.2/bin/pg_basebackup -h localhost -U xdb -p 5432 -D /data/backup -P -Xs -R
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Breakpoint 1, BaseBackup () at pg_basebackup.c:1740
1740 char *maxrate_clause = NULL;
(gdb)
连接pg_conn结构体
(gdb) n
1749 Assert(conn != NULL);
(gdb) p *conn
$1 = {pghost = 0x6282d0 "localhost", pghostaddr = 0x0, pgport = 0x6282f0 "5432", pgtty = 0x628310 "",
connect_timeout = 0x0, client_encoding_initial = 0x0, pgoptions = 0x628330 "", appname = 0x0,
fbappname = 0x628350 "pg_basebackup", dbName = 0x6282b0 "replication", replication = 0x6283d0 "true",
pguser = 0x628290 "xdb", pgpass = 0x0, pgpassfile = 0x628d30 "/home/xdb/.pgpass", keepalives = 0x0,
keepalives_idle = 0x0, keepalives_interval = 0x0, keepalives_count = 0x0, sslmode = 0x628370 "prefer",
sslcompression = 0x628390 "0", sslkey = 0x0, sslcert = 0x0, sslrootcert = 0x0, sslcrl = 0x0, requirepeer = 0x0,
krbsrvname = 0x6283b0 "postgres", target_session_attrs = 0x6283f0 "any", Pfdebug = 0x0, noticeHooks = {
noticeRec = 0x7ffff7b9eab4 <defaultNoticeReceiver>, noticeRecArg = 0x0,
noticeProc = 0x7ffff7b9eb09 <defaultNoticeProcessor>, noticeProcArg = 0x0}, events = 0x0, nEvents = 0,
eventArraySize = 0, status = CONNECTION_OK, asyncStatus = PGASYNC_IDLE, xactStatus = PQTRANS_IDLE,
queryclass = PGQUERY_SIMPLE, last_query = 0x61f1c0 "SHOW wal_segment_size", last_sqlstate = "\000\000\000\000\000",
options_valid = true, nonblocking = false, singleRowMode = false, copy_is_binary = 0 '\000', copy_already_done = 0,
notifyHead = 0x0, notifyTail = 0x0, nconnhost = 1, whichhost = 0, connhost = 0x627a50, sock = 7, laddr = {addr = {
ss_family = 10, __ss_padding = "\307\326", '\000' <repeats 19 times>, "\001", '\000' <repeats 95 times>,
__ss_align = 0}, salen = 28}, raddr = {addr = {ss_family = 10,
__ss_padding = "\025\070", '\000' <repeats 19 times>, "\001", '\000' <repeats 95 times>, __ss_align = 0},
salen = 28}, pversion = 196608, sversion = 110002, auth_req_received = true, password_needed = false,
sigpipe_so = false, sigpipe_flag = true, try_next_addr = false, try_next_host = false, addr_cur = 0x0,
setenv_state = SETENV_STATE_IDLE, next_eo = 0x0, send_appname = true, be_pid = 1435, be_key = -828773845,
pstatus = 0x629570, client_encoding = 0, std_strings = true, verbosity = PQERRORS_DEFAULT,
show_context = PQSHOW_CONTEXT_ERRORS, lobjfuncs = 0x0, inBuffer = 0x61f600 "T", inBufSize = 16384, inStart = 75,
inCursor = 75, inEnd = 75, outBuffer = 0x623610 "Q", outBufSize = 16384, outCount = 0, outMsgStart = 1, outMsgEnd = 27,
rowBuf = 0x627620, rowBufLen = 32, result = 0x0, next_result = 0x0, sasl_state = 0x0, ssl_in_use = false,
allow_ssl_try = false, wait_ssl_try = false, ssl = 0x0, peer = 0x0, engine = 0x0, gctx = 0x0, gtarg_nam = 0x0,
errorMessage = {data = 0x627830 "", len = 0, maxlen = 256}, workBuffer = {data = 0x627940 "SELECT", len = 6,
maxlen = 256}, addrlist = 0x0, addrlist_family = 0}
(gdb)
判断版本,是否支持BaseBackup
(gdb) n
1755 minServerMajor = 901;
(gdb)
1756 maxServerMajor = PG_VERSION_NUM / 100;
(gdb) p PG_VERSION_NUM
$2 = 110002
(gdb) n
1757 serverVersion = PQserverVersion(conn);
(gdb)
1758 serverMajor = serverVersion / 100;
(gdb)
1759 if (serverMajor < minServerMajor || serverMajor > maxServerMajor)
(gdb) p serverVersion
$3 = 110002
(gdb) n
判断服务器是否支持WAL streaming
(gdb) n
1772 if (includewal == STREAM_WAL && !CheckServerVersionForStreaming(conn))
(gdb)
如需要,生成recovery.conf文件
1785 if (writerecoveryconf)
(gdb) p includewal
$4 = STREAM_WAL
(gdb) p writerecoveryconf
$5 = true
(gdb) n
1786 GenerateRecoveryConf(conn);
(gdb)
获取系统标识符
1791 if (!RunIdentifySystem(conn, &sysidentifier, &latesttli, NULL, NULL))
(gdb)
1797 PQescapeStringConn(conn, escaped_label, label, sizeof(escaped_label), &i);
(gdb) p sysidentifier
$6 = 0x6292d0 "6662151435832250464"
(gdb) p *sysidentifier
$7 = 54 '6'
(gdb) p latesttli
$8 = 1
(gdb)
开始实际的备份工作
(gdb) p escaped_label
$9 = "pg_basebackup base backup\000\000\000\001", '\000' <repeats 27 times>, "\"`-\360\377\177\000\000\000\000\000\000\377\177\000\000` u\367\377\177\000\000\000\001\000\000\000\000\000\000\001\000\000\000\002\000\000\000]VA\000\000\000\000\000@\335\377\377\377\177\000\000b\343\377\377\377\177", '\000' <repeats 35 times>, "\343\377\377\377\177\000\000B\335\377\377\377\177\000\000\370Ǹ\367\377\177\000\000\220\325\227\367\377\177\000\000\000\341\377\377\377\177\000\000\000\000\000\000\000\000\000\000\371D"...
(gdb) p label
$10 = 0x412610 "pg_basebackup base backup"
(gdb) p i
$11 = 0
(gdb)
构造backup命令
(gdb) n
1802 if (verbose)
(gdb)
1807 if (showprogress && !verbose)
(gdb)
1809 fprintf(stderr, "waiting for checkpoint");
(gdb)
waiting for checkpoint1810 if (isatty(fileno(stderr)))
(gdb)
1811 fprintf(stderr, "\r");
(gdb)
1817 psprintf("BASE_BACKUP LABEL '%s' %s %s %s %s %s %s %s",
(gdb)
1824 format == 't' ? "TABLESPACE_MAP" : "",
(gdb)
1817 psprintf("BASE_BACKUP LABEL '%s' %s %s %s %s %s %s %s",
(gdb)
1822 includewal == NO_WAL ? "" : "NOWAIT",
(gdb)
1817 psprintf("BASE_BACKUP LABEL '%s' %s %s %s %s %s %s %s",
(gdb)
1820 includewal == FETCH_WAL ? "WAL" : "",
(gdb)
1817 psprintf("BASE_BACKUP LABEL '%s' %s %s %s %s %s %s %s",
(gdb)
1816 basebkp =
(gdb)
1827 if (PQsendQuery(conn, basebkp) == 0)
(gdb)
(gdb) p basebkp
$12 = 0x6291f0 "BASE_BACKUP LABEL 'pg_basebackup base backup' PROGRESS NOWAIT "
(gdb)
发送命令到服务器端,获取执行结果
(gdb) n
1837 res = PQgetResult(conn);
(gdb)
1838 if (PQresultStatus(res) != PGRES_TUPLES_OK)
(gdb)
返回结果
(gdb) p *res
$13 = {ntups = 1, numAttributes = 2, attDescs = 0x629688, tuples = 0x629e90, tupArrSize = 128, numParameters = 0,
paramDescs = 0x0, resultStatus = PGRES_TUPLES_OK,
cmdStatus = "SELECT\000\000\000\000\000\000\000\000\000\000\027\000\000\000\004\000\000\000\377\377\377\377:\226b", '\000' <repeats 17 times>, "\031\000\000\000\377\377\377\377\377\377\377\377B\226b", binary = 0, noticeHooks = {
noticeRec = 0x7ffff7b9eab4 <defaultNoticeReceiver>, noticeRecArg = 0x0,
noticeProc = 0x7ffff7b9eb09 <defaultNoticeProcessor>, noticeProcArg = 0x0}, events = 0x0, nEvents = 0,
client_encoding = 0, errMsg = 0x0, errFields = 0x0, errQuery = 0x0, null_field = "", curBlock = 0x629680,
curOffset = 133, spaceLeft = 1915}
(gdb)
(gdb) p *res->attDescs
$14 = {name = 0x6296c8 "recptr", tableid = 0, columnid = 0, format = 0, typid = 25, typlen = -1, atttypmod = 0}
(gdb) p *res->tuples
$15 = (PGresAttValue *) 0x6296d8
(gdb) p **res->tuples
$16 = {len = 10, value = 0x6296f8 "1/57000028"}
(gdb) p *res->tuples[2]
Cannot access memory at address 0x0
(gdb) p *res->tuples[0]
$17 = {len = 10, value = 0x6296f8 "1/57000028"}
(gdb) p *res->tuples[1]
Cannot access memory at address 0x15171
(gdb)
判断ntuples,获取WAL start位置
(gdb) n
1844 if (PQntuples(res) != 1)
(gdb)
1852 strlcpy(xlogstart, PQgetvalue(res, 0, 0), sizeof(xlogstart));
(gdb) p PQgetvalue(res, 0, 0)
$18 = 0x6296f8 "1/57000028"
(gdb) p xlogstart
$19 = " `-\360\377\177\000\000\353\340\377\377\377\177\000\000\360\340a", '\000' <repeats 13 times>, "\360\337\377\377\377\177", '\000' <repeats 25 times>
(gdb) n
1854 if (verbose)
(gdb) p xlogstart
$20 = "1/57000028\000\377\377\177\000\000\360\340a", '\000' <repeats 13 times>, "\360\337\377\377\377\177", '\000' <repeats 25 times>
(gdb)
获取时间线timeline
(gdb) n
1862 if (PQnfields(res) >= 2)
(gdb) p PQnfields(res)
$21 = 2
(gdb) n
1863 starttli = atoi(PQgetvalue(res, 0, 1));
(gdb)
1866 PQclear(res);
(gdb) p atoi(PQgetvalue(res, 0, 1))
$22 = 1
(gdb) p res->tuples[1]
$23 = (PGresAttValue *) 0x15171
(gdb) p res->tuples[0]
$24 = (PGresAttValue *) 0x6296d8
(gdb)
(gdb) n
1867 MemSet(xlogend, 0, sizeof(xlogend));
(gdb)
1869 if (verbose && includewal != NO_WAL)
(gdb) p xlogend
$25 = '\000' <repeats 63 times>
Get the header
(gdb) n
1876 res = PQgetResult(conn);
(gdb) n
1877 if (PQresultStatus(res) != PGRES_TUPLES_OK)
(gdb) p *res
$26 = {ntups = 1, numAttributes = 3, attDescs = 0x629688, tuples = 0x629e90, tupArrSize = 128, numParameters = 0,
paramDescs = 0x0, resultStatus = PGRES_TUPLES_OK,
cmdStatus = "SELECT\000\000\000\000\000\000\000\000\000\000\027\000\000\000\004\000\000\000\377\377\377\377:\226b", '\000' <repeats 17 times>, "\031\000\000\000\377\377\377\377\377\377\377\377B\226b", binary = 0, noticeHooks = {
noticeRec = 0x7ffff7b9eab4 <defaultNoticeReceiver>, noticeRecArg = 0x0,
noticeProc = 0x7ffff7b9eb09 <defaultNoticeProcessor>, noticeProcArg = 0x0}, events = 0x0, nEvents = 0,
client_encoding = 0, errMsg = 0x0, errFields = 0x0, errQuery = 0x0, null_field = "", curBlock = 0x629680,
curOffset = 183, spaceLeft = 1865}
统计总大小,用于进度报告
(gdb)
1892 totalsize = totaldone = 0;
(gdb) n
1893 tablespacecount = PQntuples(res);
(gdb)
1894 for (i = 0; i < PQntuples(res); i++)
(gdb) p tablespacecount
$29 = 1
(gdb) n
1896 totalsize += atol(PQgetvalue(res, i, 2));
(gdb) p PQgetvalue(res, i, 2)
$30 = 0x629730 "445480"
(gdb) p atol(PQgetvalue(res, i, 2))
$31 = 445480
(gdb) n
1903 if (format == 'p' && !PQgetisnull(res, i, 1))
(gdb)
1894 for (i = 0; i < PQntuples(res); i++)
(gdb) p res->tuples[0][0]
$33 = {len = -1, value = 0x629658 ""}
(gdb) p res->tuples[0][1]
$34 = {len = -1, value = 0x629658 ""}
(gdb) p res->tuples[0][2]
$35 = {len = 6, value = 0x629730 "445480"}
(gdb)
开始接收实际的数据chunks前,开始streaming session.
(gdb) n
1914 if (format == 't' && strcmp(basedir, "-") == 0 && PQntuples(res) > 1)
(gdb)
1926 if (includewal == STREAM_WAL)
(gdb)
1928 if (verbose)
(gdb)
1931 StartLogStreamer(xlogstart, starttli, sysidentifier);
(gdb) n
Detaching after fork from child process 1511.
1937 for (i = 0; i < PQntuples(res); i++)
(gdb)
查看操作系统中的日志目录
[xdb@localhost backup]$ ll
total 0
drwx------. 3 xdb xdb 60 Mar 15 15:46 pg_wal
[xdb@localhost backup]$ ll ./pg_wal/
total 16384
-rw-------. 1 xdb xdb 16777216 Mar 15 15:46 000000010000000100000057
drwx------. 2 xdb xdb 6 Mar 15 15:46 archive_status
[xdb@localhost backup]$
Start receiving chunks,开始接收chunks
(gdb) n
Detaching after fork from child process 1511.
1937 for (i = 0; i < PQntuples(res); i++)
(gdb) n
1939 if (format == 't')
(gdb)
1942 ReceiveAndUnpackTarFile(conn, res, i);
(gdb)
193789/445489 kB
(gdb) for (i = 0; i < PQntuples(res); i++)
查看操作系统中的备份目录
[xdb@localhost backup]$ ll
total 56
-rw-------. 1 xdb xdb 226 Mar 15 15:47 backup_label
drwx------. 6 xdb xdb 58 Mar 15 15:48 base
drwx------. 2 xdb xdb 4096 Mar 15 15:48 global
drwx------. 2 xdb xdb 6 Mar 15 15:47 pg_commit_ts
drwx------. 2 xdb xdb 6 Mar 15 15:47 pg_dynshmem
-rw-------. 1 xdb xdb 4513 Mar 15 15:48 pg_hba.conf
-rw-------. 1 xdb xdb 1636 Mar 15 15:48 pg_ident.conf
drwx------. 4 xdb xdb 68 Mar 15 15:48 pg_logical
drwx------. 4 xdb xdb 36 Mar 15 15:47 pg_multixact
drwx------. 2 xdb xdb 6 Mar 15 15:47 pg_notify
drwx------. 2 xdb xdb 6 Mar 15 15:48 pg_replslot
drwx------. 2 xdb xdb 6 Mar 15 15:47 pg_serial
drwx------. 2 xdb xdb 6 Mar 15 15:47 pg_snapshots
drwx------. 2 xdb xdb 6 Mar 15 15:48 pg_stat
drwx------. 2 xdb xdb 6 Mar 15 15:48 pg_stat_tmp
drwx------. 2 xdb xdb 6 Mar 15 15:47 pg_subtrans
drwx------. 2 xdb xdb 6 Mar 15 15:48 pg_tblspc
drwx------. 2 xdb xdb 6 Mar 15 15:47 pg_twophase
-rw-------. 1 xdb xdb 3 Mar 15 15:48 PG_VERSION
drwx------. 3 xdb xdb 92 Mar 15 15:48 pg_wal
drwx------. 2 xdb xdb 18 Mar 15 15:48 pg_xact
-rw-------. 1 xdb xdb 88 Mar 15 15:48 postgresql.auto.conf
-rw-------. 1 xdb xdb 23812 Mar 15 15:48 postgresql.conf
-rw-------. 1 xdb xdb 183 Mar 15 15:48 recovery.conf
[xdb@localhost backup]$
显示进度
(gdb) n
1945 if (showprogress)
(gdb)
1947 progress_report(PQntuples(res), NULL, true);
(gdb)
194889/445489 kB (100%),if (isatty(fileno(stderr)))
(gdb)
(gdb) n
1949 fprintf(stderr, "\n");
(gdb)
1952 PQclear(res);
(gdb)
Get the stop position
(gdb)
1957 res = PQgetResult(conn);
(gdb) n
1958 if (PQresultStatus(res) != PGRES_TUPLES_OK)
(gdb) p *res
$36 = {ntups = 1, numAttributes = 2, attDescs = 0x6295a8, tuples = 0x629db0, tupArrSize = 128, numParameters = 0,
paramDescs = 0x0, resultStatus = PGRES_TUPLES_OK,
cmdStatus = "SELECT", '\000' <repeats 54 times>, "\300!", <incomplete sequence \367>, binary = 0, noticeHooks = {
noticeRec = 0x7ffff7b9eab4 <defaultNoticeReceiver>, noticeRecArg = 0x0,
noticeProc = 0x7ffff7b9eb09 <defaultNoticeProcessor>, noticeProcArg = 0x0}, events = 0x0, nEvents = 0,
client_encoding = 0, errMsg = 0x0, errFields = 0x0, errQuery = 0x0, null_field = "", curBlock = 0x6295a0,
curOffset = 133, spaceLeft = 1915}
(gdb)
(gdb) n
1965 if (PQntuples(res) != 1)
(gdb)
1972 strlcpy(xlogend, PQgetvalue(res, 0, 0), sizeof(xlogend));
(gdb) p xlogend
$37 = '\000' <repeats 63 times>
(gdb) p *xlogend
$38 = 0 '\000'
(gdb) n
1973 if (verbose && includewal != NO_WAL)
(gdb)
1975 PQclear(res);
(gdb)
COMMAND is OK
(gdb)
1977 res = PQgetResult(conn);
(gdb)
1978 if (PQresultStatus(res) != PGRES_COMMAND_OK)
(gdb) p *res
$39 = {ntups = 0, numAttributes = 0, attDescs = 0x0, tuples = 0x0, tupArrSize = 0, numParameters = 0, paramDescs = 0x0,
resultStatus = PGRES_COMMAND_OK, cmdStatus = "SELECT", '\000' <repeats 54 times>, "\300!", <incomplete sequence \367>,
binary = 0, noticeHooks = {noticeRec = 0x7ffff7b9eab4 <defaultNoticeReceiver>, noticeRecArg = 0x0,
noticeProc = 0x7ffff7b9eb09 <defaultNoticeProcessor>, noticeProcArg = 0x0}, events = 0x0, nEvents = 0,
client_encoding = 0, errMsg = 0x0, errFields = 0x0, errQuery = 0x0, null_field = "", curBlock = 0x0, curOffset = 0,
spaceLeft = 0}
(gdb)
善后工作,如在备份结束后,持久化数据在磁盘上等
(gdb) n
1997 if (bgchild > 0)
(gdb)
2014 if (verbose)
(gdb)
2019 if (write(bgpipe[1], xlogend, strlen(xlogend)) != strlen(xlogend))
(gdb)
2028 r = waitpid(bgchild, &status, 0);
(gdb) p bgchild
$40 = 1511
(gdb) n
2029 if (r == -1)
(gdb)
2035 if (r != bgchild)
(gdb) p r
$41 = 1511
(gdb) n
2041 if (!WIFEXITED(status))
(gdb)
2047 if (WEXITSTATUS(status) != 0)
(gdb)
2098 destroyPQExpBuffer(recoveryconfcontents);
(gdb)
2103 PQclear(res);
(gdb)
2104 PQfinish(conn);
(gdb)
2113 if (do_sync)
(gdb)
2115 if (format == 't')
(gdb)
2122 (void) fsync_pgdata(basedir, progname, serverVersion);
(gdb)
2126 if (verbose)
(gdb)
2128 }
(gdb)
main (argc=12, argv=0x7fffffffe4b8) at pg_basebackup.c:2534
2534 success = true;
(gdb)
再次启动跟踪,监控后台数据库的活动.
进入BaseBackup函数
Breakpoint 1, BaseBackup () at pg_basebackup.c:1740
1740 char *maxrate_clause = NULL;
(gdb)
数据库活动
15:56:25 (xdb@[local]:5432)testdb=# select * from pg_stat_activity
[local] xdb@testdb-# where backend_type not in ('walwriter', 'checkpointer', 'background writer', 'logical replication launcher', 'autovacuum launcher') and query not like '%pg_stat_activity%';
-[ RECORD 1 ]----+------------------------------
datid |
datname |
pid | 1566
usesysid | 10
usename | xdb
application_name | pg_basebackup
client_addr | ::1
client_hostname |
client_port | 51162
backend_start | 2019-03-15 15:56:13.82013+08
xact_start |
query_start |
state_change | 2019-03-15 15:56:13.821507+08
wait_event_type | Client
wait_event | ClientRead
state | idle
backend_xid |
backend_xmin |
query |
backend_type | walsender
开启WAL streaming
1931 StartLogStreamer(xlogstart, starttli, sysidentifier);
(gdb)
Detaching after fork from child process 1602.
1937 for (i = 0; i < PQntuples(res); i++)
(gdb)
数据库活动
16:01:25 (xdb@[local]:5432)testdb=# select * from pg_stat_activity
where backend_type not in ('walwriter', 'checkpointer', 'background writer', 'logical replication launcher', 'autovacuum launcher') and query not like '%pg_stat_activity%';
-[ RECORD 1 ]----+------------------------------
datid |
datname |
pid | 1566
usesysid | 10
usename | xdb
application_name | pg_basebackup
client_addr | ::1
client_hostname |
client_port | 51162
backend_start | 2019-03-15 15:56:13.82013+08
xact_start |
query_start |
state_change | 2019-03-15 16:00:47.345326+08
wait_event_type | Client
wait_event | ClientWrite
state | active
backend_xid |
backend_xmin |
query |
backend_type | walsender
-[ RECORD 2 ]----+------------------------------
datid |
datname |
pid | 1601
usesysid | 10
usename | xdb
application_name | pg_basebackup
client_addr | ::1
client_hostname |
client_port | 51164
backend_start | 2019-03-15 16:01:47.150434+08
xact_start |
query_start |
state_change | 2019-03-15 16:01:47.159234+08
wait_event_type | Activity
wait_event | WalSenderMain
state | active
backend_xid |
backend_xmin |
query |
backend_type | walsender
16:01:56 (xdb@[local]:5432)testdb=#
16:01:56 (xdb@[local]:5432)testdb=
拷贝数据
(gdb)
1942 ReceiveAndUnpackTarFile(conn, res, i);
(gdb)
193789/445489 kBfor (i = 0; i < PQntuples(res); i++)
(gdb)
1945 if (showprogress)
(gdb)
数据库活动
...
-[ RECORD 3 ]----+------------------------------
datid |
datname |
pid | 1352
usesysid |
usename |
application_name |
client_addr |
client_hostname |
client_port |
backend_start | 2019-03-15 15:03:01.923092+08
xact_start |
query_start |
state_change |
wait_event_type | Activity
wait_event | WalWriterMain
state |
backend_xid |
backend_xmin |
query |
backend_type | walwriter
执行善后工作
2113 if (do_sync)
(gdb)
2115 if (format == 't')
(gdb)
2122 (void) fsync_pgdata(basedir, progname, serverVersion);
(gdb)
2126 if (verbose)
(gdb)
2128 }
(gdb)
数据库活动
16:05:01 (xdb@[local]:5432)testdb=# select * from pg_stat_activity
where backend_type not in ('checkpointer', 'background writer', 'logical replication launcher', 'autovacuum launcher') and query not like '%pg_stat_activity%';
-[ RECORD 1 ]----+------------------------------
datid |
datname |
pid | 1352
usesysid |
usename |
application_name |
client_addr |
client_hostname |
client_port |
backend_start | 2019-03-15 15:03:01.923092+08
xact_start |
query_start |
state_change |
wait_event_type | Activity
wait_event | WalWriterMain
state |
backend_xid |
backend_xmin |
query |
backend_type | walwriter
DONE!
四、参考资料
PG Source Code
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
软考中级精品资料免费领
- 历年真题答案解析
- 备考技巧名师总结
- 高频考点精准押题
- 资料下载
- 历年真题
193.9 KB下载数265
191.63 KB下载数245
143.91 KB下载数1148
183.71 KB下载数642
644.84 KB下载数2756