原文出处 http://mysql.taobao.org/monthly/2015/10/04/mysql
PostgreSQL在9.0以后引入了主备流复制机制,经过流复制,备库不断的从主库同步相应的数据,并在备库apply每一个WAL record,这里的流复制每次传输单位是WAL日志的record。而PostgreSQL9.0以前提供的方法是主库写完一个WAL日志文件后,才把WAL日志文件传送到备库,这样的方式致使主备延迟特别大。同时PostgreSQL9.0以后提供了Hot Standby,备库在应用WAL record的同时也可以提供只读服务,大大提高了用户体验。sql
PG主备流复制的核心部分由walsender,walreceiver和startup三个进程组成。
walsender进程是用来发送WAL日志记录的,执行顺序以下:app
PostgresMain()->exec_replication_command()->StartReplication()->WalSndLoop()->XLogSendPhysical()
walreceiver进程是用来接收WAL日志记录的,执行顺序以下:框架
sigusr1_handler()->StartWalReceiver()->AuxiliaryProcessMain()->WalReceiverMain()->walrcv_receive()
startup进程是用来apply日志的,执行顺序以下:ide
PostmasterMain()->StartupDataBase()->AuxiliaryProcessMain()->StartupProcessMain()->StartupXLOG()
下图是PG主备整体框架图:函数
walsender和walreceiver交互主要分为如下几个步骤:oop
startup进程进入standby模式和apply日志主要过程:spa
对读取的WAL record进行redo,经过record->xl_rmid
信息,调用相应的redo资源管理器进行redo操做。好比heap_redo的XLOG_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掉。
5. 检查一致性,若是一致了,Hot Standby模式能够接受用户只读查询;更新共享内存中XLogCtlData的apply位点和时间线;若是恢复到时间点,时间线或者事务id须要检 查是否恢复到当前目标;
6. 回到步骤3,读取next WAL record。code