#流复制原理:
postgres数据库流复制是基于wal日志传送技术实现同步,主节点(master)启用walsender进程持续发送wal日志流,备节点(standby)通过walreceiver进程实时接受从主传过的wal日志流,并且通过walreceiver进程调用内部函数write()和fsync()将wal数据全部写入wal segment和刷新到wal segment,并通知starup进程回放已经写入wal segment的wal数据;后面我会详细讲解整个流复制内部结构。
#了解一下同步和异步流复制:
同步和异步流复制说的是备库wal日志应用。
同步流复制:字面意思就是和主库wal日志流应用保持一致,主备落盘数据一致;如果想要保持主备数据强一致性,需要单独设置synchronous_commit参数为remote_apply,这个我们后面会讲到。
异步流复制:允许备库有延迟应用wal日志流,同一时间点查询主备数据可能存在数据不一致情况,一般在性能好的服务器上,延迟都是毫秒级的。
#流复制内部原理解读
本案例我们以insert操作为例:
1.后台进程通过执行XLogInsert()和 XLogFlush()函数将wal数据写入并刷新到wal segment。 2.walsender进行将写入wal segment的wal数据发送到walreceiver进程。
3.主节点发送wal数据后,后台进程等待从备节点的ACK响应,更确切的说,后台进程执行内部函数SyncRepWaitForLSN()来获取latch锁,并等待锁释放。
4.备节点的walreceiver进程使用write()函数将wal数据写入到wal segment,并且返回ACK响应给walsender。
5.备节点的walreceiver进程使用fsync()函数将wal数据全部刷新到wal segment,并且返回又一个ACK响应给walsengder,通知startup进程wal已经更新。
6.starup进程回放已经写入wal segment的wal数据。
7.walsender进程在接受到ACK响应后释放latch锁,然后,后台进程提交或者终止的操作就完成了。latch释放的时间依赖于参数synchronous_commit,如果参数是on,则在以上第五步接受ACK响应后释放latch,如果设置是remote_write,那么在第四步接受ACK响应后就释放latch。 synchronous_commit参数: ON:必须要等事务日志刷新到本地磁盘,并且还要等远程备库也提交到磁盘才能返回客户端已经提交 remote_write:当事务提交时,不仅要把wal刷新到磁盘,还需要等wal日志发送到备库,但不用等备库刷新wal到磁盘。(一般建议这样设置)
#流复制复制相关进程:
1.walsender进程:
1>.primary主节点启用walsender进程
2>.walsender进程用于发送wal segment的wal数据发送到walreceiver进程
3>.walsender进程用于响应由walreceiver进程返回的ACK确认
4>.walsender进程在接受到ACK响应后释放latch锁
2.walreceiver进程:
1>.备节点启用walreceiver进程
2>.walreceiver进程用于接收由walsender进程发送wal segment的wal数据
3>.walreceiver进程调用write()函数将wal数据写入到wal segment,并且返回ACK响应给walsender
4>.walreceiver进程使用fsync()函数将wal数据全部刷新到wal segment,并且返回又一个ACK响应给walsengder
5>.walreceiver进程通知startup进程进行回放已经写入wal segment的wal数据
6>.walreceiver进程还定期作为备节点的心跳发送ACK响应;心跳发送间隔通过wal_receiver_status_interval设置,默认10秒
3.startup进程:
1>.starup进程回放已经写入wal segment的wal数据
4.备节点发送主节点每一个ACK包含以下内容:
已经写入的最新WAL数据的LSN的位置
已经刷新最新的WAL数据的LSN的位置
startup进程最新回放wal数据的LSN位置
发送ACK的时间戳
# 流复制架构:
早期postgres数据库只支持1+N的异步流复制,但不能提供业务读写,只能做为单独备库应用wal文件;从9.1版本开始支持1+1+N的同步备和异步备,并添加Hot Standby参数,使备库以只读的方式进行查询;9.2版本开始支持级联流复制。
一主一从:
1>. 两节点的数据可以保持一致,从库可以用来承担只读业务
2>.主节点能最大化对外提供高效服务,因为从库承担一部分业务
3>.弊端就是如果从库异常或宕机,导致主节点不能接收到从库walreceiver进程返回的ACK确认,导致主库的事务会一直hang的情况,不能对外提供完整业务。
一主一从一备:
1>.从节点和备节点都可以提供只读业务,对于业务数据一致性高的使用从库承担业务,如果业务数据对数据一致性高没有那么高的话,可以使用异步备库承担业务
2>.主从节点都能最大化对外提供高效服务,因为备库承担一部分业务
3>.这种架构不会出现像主库事务hang现象,从库如果宕机,异步备库会自动拉成从库,承担同步备角色,继续使用walreceiver进程给主节点返回的ACK确认,备库异常或宕机不影响主从节点服务。
# Primary如何管理多个备用节点
主服务器单独等待来自同步备用服务器的ACK响应。换句话说,主服务器仅确认同步备用的WAL数据的写入和刷新。因此,流复制可以确保只有同步备用服务器与主服务器处于一致和同步的状态。 如图显示了潜在备用设备的ACK响应早于主备用设备的返回的情况。在这种情况下,主服务器不会完成当前事务的提交操作,而是继续等待主服务器的ACK响应。当接收到主进程的响应时,后端进程释放闩锁并完成当前事务处理。
1) 图中1,主节点的后端数据库进程继续等待来自同步备数据库服务器返回的ACK响应,即使它已经从潜在的异步备节点接收到ACK响应。
2) 图中2, 在接收到来自同步备节点服务器的ACK响应后,主服务器的后端进程释放锁存器并完成当前事务处理。
Behavior When a Failure Occurs(发生故障时的行为)?
让我们看看备用服务器出现故障时主服务器的行为?
1).当潜在或异步备用服务器出现故障时,主服务器终止连接到出现故障的备用服务器的walsender进程,并继续所有处理。换句话说,主服务器上的事务处理不会受到任何一种备用服务器故障的影响。
2).当同步备用服务器出现故障时,主服务器终止连接到出现故障的备用服务器的walsender进程,并用最高优先级的潜在备用服务器替换同步备用服务器。见图。与上述故障相反,主服务器上的查询处理将从故障点暂停到更换同步备用服务器。(因此,备用服务器的故障检测是提高复制系统可用性的一项非常重要的功能。故障检测将在下一节中描述。)
3).在任何情况下,如果主拥有多个备节点数据库以同步模式运行,则主节点始终只保留一个优先级最高的同步备节点,并且同步备节点始终与主节点的数据处于一致和同步的状态。
# 检测备用服务器的故障
1)备用服务器进程故障检测:当检测到walsender和walreceiver之间的连接断开时,主服务器立即确定备用服务器或walreceiver进程故障。当一个low level网络函数在写或读walreceiver的套接字时返回一个错误,主函数也会立即判断出备用服务器进程故障了。
2)如果walreceiver在为参数wal_sender_timeout设置的时间内(默认为60秒)没有返回任何内容,则主服务器确定备用服务器有故障。
与上述故障相反,即使备用服务器由于某些故障(例如,备用服务器的硬件故障、网络故障等)而不再能够发送任何响应,也需要一定的时间,直到wal_sender_timeout秒,才能在主服务器上确认备用服务器的故障。
注:根据故障类型的不同,通常可以在故障发生后立即检测到。然而,在故障发生和故障检测之间可能存在时间滞后。特别是,如果后一种类型的故障发生在同步备用服务器中,则主服务器上的所有事务处理都将停止,直到检测到备用服务器的故障。
# 关于synchronous_commit参数设置
当设置为remote_apply时,提交将等待,直到来自当前同步备用的回复表明他们已经接收到事务的提交记录并应用了它,以便备用上的查询可以看到它,并将其写入备用上的持久存储中。这将导致比以前的设置更大的提交延迟,因为它在等待WAL重放。
当设置为on时,提交等待,直到来自当前同步备用的回复表明他们已经收到事务的提交记录并将其刷新到持久存储。这确保了事务不会丢失,除非主备用和所有同步备用的数据库存储都遭到破坏。
# postgres流复制总结:
1.流复制三节点架构能保证数据库高可用,保证数据库备节点异常,不影响主库持续对外提供服务。再配合高可用中间件使用,完全能保证数据库对外提供服务。例如patroni+etcd架构、pgpool+wacthdog架构等
2.如果流复制应用参数设置为remote_apply,可以保证主备数据具有强一致特征,主备数据实时保持一致状态;但带来的危害就是主库事务提交时间变的更长,导致前端业务等待时间比较长。
3.一般数据库流复制应用参数默认为on级别,不能完全保证主备数据具有强一致特征,主备数据可能会有不一致情况;一般情况对于主机性能比较好的服务器承载数据库备库的话,在高并发业务情况下,同步备库和主库的数据的数据差应该在毫秒级别(不同服务器性能不同);有点就是保证数据不丢失。
4.根据业务对实时性要求,把业务均衡分散到各个节点上。