关于postgresql:特性分析-PostgreSQL-PrimaryStandby-主备流复制机制

9次阅读

共计 3036 个字符,预计需要花费 8 分钟才能阅读完成。

本文首发于 2015-11-21 20:02:26

引言

PostgreSQL 在 9.0 之后引入了主备流复制机制,通过流复制,备库一直的从主库同步相应的数据,并在备库 apply 每个 WAL record,这里的流复制每次传输单位是 WAL 日志的 record。而 PostgreSQL 9.0 之前提供的办法是主库写完一个 WAL 日志文件后,才把 WAL 日志文件传送到备库,这样的形式导致主备提早特地大。同时,PostgreSQL 9.0 之后提供了 Hot Standby,备库在利用 WAL record 的同时也可能提供只读服务,大大晋升了用户体验。

主备总体构造

PostgreSQL 主备流复制的外围局部由 walsenderwalreceiverstartup 三个过程组成。

walsender 过程是用来发送 WAL 日志记录的,执行程序如下:

PostgresMain()->exec_replication_command()->StartReplication()->WalSndLoop()->XLogSendPhysical()

walreceiver 过程是用来接管 WAL 日志记录的,执行程序如下:

sigusr1_handler()->StartWalReceiver()->AuxiliaryProcessMain()->WalReceiverMain()->walrcv_receive()

startup 过程是用来 apply 日志的,执行程序如下:

PostmasterMain()->StartupDataBase()->AuxiliaryProcessMain()->StartupProcessMain()->StartupXLOG()

walsender 和 walreceiver 过程流复制过程

walsender 和 walreceiver 交互次要分为以下几个步骤:

  1. walreceiver 启动后通过 recovery.conf 文件中的 primary_conninfo 参数信息连向主库,主库通过连贯参数 replication=true 启动 walsender 过程;
  2. walreceiver 执行 identify_system 命令,获取主库 systemid/timeline/xlogpos 等信息,执行 TIMELINE_HISTORY 命令拉取 history 文件;
  3. 执行 wal_startstreaming 开始启动流复制,通过 walrcv_receive 获取 WAL 日志,期间也会回应主库发过来的心跳信息(接管位点、flush 位点、apply 位点),向主库发送 feedback 信息(最老的事务 id),防止 vacuum 删掉备库正在应用的记录;
  4. 执行 walrcv_endstreaming 完结流复制,期待 startup 过程更新 receiveStartreceiveStartTLI,一旦更新,进入步骤 2。

walreceiver 和 startup 过程

startup 过程进入 standby 模式和 apply 日志次要过程:

  1. 读取 pg_control 文件,找到 redo 位点;读取 recovery.conf,如果配置 standby_mode=on 则进入 standby 模式。
  2. 如果是 Hot Standby 须要初始化 clog、subtrans、事务环境等。初始化 redo 资源管理器,比方 Heap、Heap2、Database、XLOG 等。
  3. 读取 WAL record,如果 record 不存在须要调用 XLogPageRead->WaitForWALToBecomeAvailable->RequestXLogStreaming 唤醒 walreceiver 从 walsender 获取 WAL record。
  4. 对读取的 WAL record 进行 redo,通过 record->xl_rmid 信息,调用相应的 redo 资源管理器进行 redo 操作。比方 heap_redoXLOG_HEAP_INSERT 操作,就是通过 record 的信息在 buffer page 中减少一个 record:
MemSet((char *) htup, 0, sizeof(HeapTupleHeaderData));
 /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
 memcpy((char *) htup + offsetof(HeapTupleHeaderData, t_bits),
        (char *) xlrec + SizeOfHeapInsert + SizeOfHeapHeader,
        newlen);
 newlen += offsetof(HeapTupleHeaderData, t_bits);
 htup->t_infomask2 = xlhdr.t_infomask2;
 htup->t_infomask = xlhdr.t_infomask;
 htup->t_hoff = xlhdr.t_hoff;
 HeapTupleHeaderSetXmin(htup, record->xl_xid);
 HeapTupleHeaderSetCmin(htup, FirstCommandId);
 htup->t_ctid = xlrec->target.tid;

 offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true);
 if (offnum == InvalidOffsetNumber)
     elog(PANIC, "heap_insert_redo: failed to add tuple");

 freespace = PageGetHeapFreeSpace(page);        /* needed to update FSM below */

 PageSetLSN(page, lsn);

 if (xlrec->flags & XLOG_HEAP_ALL_VISIBLE_CLEARED)
     PageClearAllVisible(page);

 MarkBufferDirty(buffer);

还有局部 redo 操作 (vacuum 产生的 record) 须要查看在 Hot Standby 模式下的查问抵触,比方某些 tuples 须要 remove,而存在正在执行的 query 可能读到这些 tuples,这样就会毁坏事务隔离级别。通过函数 ResolveRecoveryConflictWithSnapshot 检测抵触,如果发生冲突,那么就把这个 query 所在的过程 kill 掉。

  1. 查看一致性,如果统一了,Hot Standby 模式能够承受用户只读查问;更新共享内存中 XLogCtlData 的 apply 位点和工夫线;如果复原到工夫点,工夫线或者事务 id 须要查看是否复原到以后指标;
  2. 回到步骤 3,读取 next WAL record。

本文转自:http://mysql.taobao.org/month…


欢送关注我的微信公众号【数据库内核】:分享支流开源数据库和存储引擎相干技术。

题目 网址
GitHub https://dbkernel.github.io
知乎 https://www.zhihu.com/people/…
思否(SegmentFault) https://segmentfault.com/u/db…
掘金 https://juejin.im/user/5e9d3e…
开源中国(oschina) https://my.oschina.net/dbkernel
博客园(cnblogs) https://www.cnblogs.com/dbkernel
正文完
 0