有时需考虑新增一个从节点:如需增加副本数以提高容错能力或替换失败的副本节点。
那如何确保新的从节点和主节点数据一致?
简单地将数据文件从一个节点复制到另一个节点通常不够。主要因为客户端仍不断向DB写新数据,数据总在变化,因此常规的文件拷贝方式会导致不同节点上呈现出不同时间点的数据,这显然非我所欲也。
或许我们该考虑锁数据库(使其不可写)来使磁盘文件保持一致,但这违背高可用设计。幸好,可做到在不停机、数据服务不中断前提下完成从节点的设置:
系统中的任何节点都可能宕机,对运维而言,能在系统不中断服务的情况下重启单个节点可太妙了。目标是即使个别节点失效,也能保持系统总体持续运行,并尽可能减小节点宕机的影响。
从节点的本地磁盘都保存了副本收到的数据变更日志。若从节点崩溃并重启或主、从节点之间网络中断,则比较容易恢复:从节点可从日志中知道,在发生故障之前处理的最后一个事务。因此,从节点可以连接到主节点,并请求在从节点断开连接时发生的所有数据变更。当应用完所有这些变化后,它就赶上了主节点,并可以像以前一样继续接收数据变更流。
主节点故障则处理很棘手:
该过程就是故障切换(failover)。
故障切换可手动进行,如:
若使用异步复制,则新主节点可能没收到老主节点宕机前的所有数据。选出新主节点后,若原主节点重新上线并加入集群,新主节点在此期间可能收到冲突的写请求,因为原主节点未意识到角色变化,还会尝试同步其他从节点,但其中的一个现在已接管成为新任主节点了。对此,常见解决方案:原主节点上未完成复制的写请求就此丢弃,但这可能会违背数据更新持久化的承诺。
若DB需和其他外部存储协作,则丢弃写入的内容是很危险的操作。如GitHub的一场事故,某个数据并非完全同步的MySQL从节点被提升为主节点,DB用自增计数器将主键分配给新
建的行,但因新主节点计数器落后于原主节点( 即二者并非完全同步),它重新使用已被原主节点分配出去的某些主键,而这些主键恰好已被外部Redis所使用,导致MySQL和Redis之间数据不一致,最后一些私有数据被错误地泄露给其他用户。
某些故障场景下可能会出现两个节点同时以为自己是主节点,即脑裂,很危险哦:两个主节点都可能接受写请求,且没有冲突解决机制,最好数据就可能丢失或损坏。某些系统对此采取安全措施:当检测到两个主节点同时存在时,会强制关闭其中一个节点1,但设计粗糙的机制可能最后会导致两个节点都被关闭。
如何设置合适的超时来检测主节点失效呢?主节点失效后,超时时间越长,意味着总体恢复时间也越长。但若超时设置太短,又可能会频繁出现不必要的故障切换,如:
若系统已是高负载或网络拥塞,则不必要的故障切换可能让情况变得更糟。
这些问题其实都没简单解决方案。因此,即使软件支持自动故障切换,不少运维团队还是更愿意手动执行。
节点故障、不可靠的网络、副本一致性,持久性,可用性和延迟的各种权衡正是分布式系统核心问题。