1.3 配置新的从节点
有时需考虑新增一个从节点:
提高容错能力
或替换失败的副本节点
1.3.1 新从节点和主节点的数据一致
简单将数据文件从一个节点复制到另一个节点一般还不够。因为客户端仍不断向DB写新数据,数据总在变化,因此常规的文件拷贝方式会导致不同节点上呈现出不同时间点的数据。
或许该考虑锁DB(使其不可写),使磁盘文件保持一致,但这又违背高可用设计。
幸好,可做到在不停机、数据服务不中断前提下完成从节点的设置:
在某时刻,获取【主节点】的一个一致性快照,避免长时间的锁整个DB
大多DB都支持该功能,因其为系统备份所必需。某些场景,可能需第三方工具,如MySQL的innobackupex
将此快照复制到新的【从节点】
【从节点】连接到【主节点】并请求快照之后发生的数据变更日志
因为在第一步建快照时,快照与系统复制日志的某个确定位置相关联,该位置信息在不同系统有不同的叫法,PostgreSQL称其为log sequence number(日志序列号),MySQL称为binlog coordinates
获得日志后,从节点来应用这些快照点之后的所有数据变更,该过程称为追赶
接下来,它可继续处理主节点上新的数据变化。并重复1~4
1.4 处理节点宕机
系统的任何节点都可能宕机,对运维而言,能在系统不中断服务情况下重启单个节点可太妙了。目标是即使个别节点失效,也能保持系统总体持续运行,并尽可能减小节点宕机影响。
1.5 主从复制实现高可用
1.5.1 从节点失效:追赶恢复
【从节点】的本地磁盘都保存了副本收到的数据变更日志。若【从节点】崩溃重启或主、从节点之间网络中断,则比较容易恢复:从节点可从日志知道,在故障之前处理的最后一个事务。因此,从节点能连接到主节点,并请求在从节点断开连接时发生的所有数据变更。
当应用完所有这些变化后,就赶上了【主节点】,并能像以前一样继续接收数据变更流。
1.5.2 主节点失效:故障切换(failover)
选择某【从节点】提升为新的【主节点】
重新配置客户端,以将它们之后的写请求发给新的【主节点】
其他从节点开始接收来自新主节点的变更数据
故障切换可【手动】进行,如:
通知运维,【主节点】已宕机,请采取必要步骤创建新的主节点
或自动进行
1.5.3 自动切换过程
确认【主节点】失效。多种可能性:系统崩溃、停电或网络问题。没有方法能确切检测到底啥问题,所以大多系统都基于超时机制:节点间频繁互发心跳存活消息,若发现某节点在一段时间内(如30s)无响应,就认为它挂了(因为计划内的维护目的而故意下线主节点的场景不算)
选一个新的【主节点】。可通过选举(剩余节点多数达成共识)或由之前选定的控制器节点(controller node)指定新主节点。最佳候选节点:拥有与原主节点的数据差异最小,以最小化数据丢失风险
重新配置系统,以启用新的主节点
客户端现在需将写请求发给新主节点。若原主节点重归,可能仍认为自己是主节点,没意识到其他节点已达成共识迫使其下台。这时,系统要确保老领导认可新领导,并降级为一个从节点
1.5.4 故障切换的变数
1.5.4.1 使用异步复制
则新【主节点】可能没收到老【主节点】宕机前的所有数据。选出新【主节点】后,若原主节点重新上线并加入集群,新【主节点】在此期间可能收到冲突的写请求,因为原主节点未意识到角色变化,还会尝试同步其他从节点,但其中的一个现在已接管成为新任主节点了。
常见解决方案:原主节点上未完成复制的写请求就此丢弃,但这可能违背数据更新持久化的承诺。
若DB需和其他外部存储协作,则丢弃写入内容很危险。如GitHub的事故,某个数据并非完全同步的MySQL从节点被提升为主,DB用自增计数器将主键分配给新建的行,但因新【主节点】计数器落后于原【主节点】( 即二者并非完全同步),它重新使用已被原主节点分配出去的某些主键,而这些主键恰好已被外部Redis所使用,导致MySQL和Redis之间数据不一致,最后一些私有数据被错误地泄露给其他用户。
1.5.4.2 脑裂
有时可能出现两个节点同时以为自己是主节点:两个主节点都可能接受写请求,且无冲突解决机制,最后数据就可能丢失或损坏。
解决方案
当检测到两个主节点同时存在时,会强制关闭其中一个节点1,但设计粗糙的机制可能导致最后两个节点都被关闭了。
得设置合适超时,来检测主节点失效:
主节点失效后,超时时间越长,意味着总体恢复时间也越长。但若超时设置太短,又可能会频繁出现不必要的故障切换,如:
临时负载峰值可能导致节点响应时间超时
或网络故障可能导致数据包延迟
若系统已是高负载或网络拥塞,则不必要的故障切换可能让情况变得更糟。
这些问题其实都无简易解决方案。因此,即使软件支持自动故障切换,不少运维团队还是更愿意手动执行。
节点故障、不可靠的网络、副本一致性,持久性,可用性和延迟的各种权衡正是分布式系统核心问题。
这种机制称为 屏蔽(fencing),充满感情的术语是:爆彼之头(Shoot The Other Node In The Head, STONITH)。 ↩︎