对于金融级数据库,需要做到无损切换,即切换不丢数据,RPO=0,RTO尽可能短。
为了做到无损切换并且考虑到主机可能发生磁盘损坏且无法恢复的场景,需要用到日志复制技术,将本地日志及时同步到其他节点。实现方式有三种:
第一种是单体数据库自带的同步
或半同步
的方式,其中半同步方式具有一定的容错能力,实践中被更多采用;
第二种是将日志存储到共享存储
系统上,后者会通过多副本冗余存储保证日志的安全性,亚马逊的 Aurora和腾讯的TDSQL-C(CynosDB) 采用了这种方式,也被称为 Share Storage;
第三种是基于Paxos/Raft
的共识算法
同步日志数据,在分布式数据库中被广泛使用。代表有阿里的Oceanbase(基于Paxos)和腾讯的TDSQL3.0(基于Raft)。
无论采用哪种方式,目的都是保证在本地节点之外,至少有一份完整的日志可用于数据恢复。
MySQL从5.5开始,用插件的形式支持半同步复制。MySQL复制默认是异步的。源将事件写入其二进制日志,副本在准备就绪时请求它们。源不知道副本是否或何时检索并处理了事务,并且不能保证任何事件都会到达任何副本。对于异步复制,如果源崩溃,它已提交的事务可能不会传输到任何副本。在这种情况下,从源到副本的故障转移可能会导致故障转移到丢失与源相关的事务的服务器。
通过完全同步复制,当源提交事务时,在源返回到执行该事务的会话之前,所有副本
也已提交该事务。完全同步复制意味着可以随时从源故障转移到任何副本。完全同步复制的缺点是完成事务可能会有很多延迟。
半同步复制介于异步复制和完全同步复制之间。源等待至少一个副本接收并记录事件
(所需的副本数量是可配置的),然后提交事务。源不会等待所有副本确认接收,它只需要副本的确认
,而不需要事件已在副本侧完全执行和提交。因此,半同步复制可以保证,如果源崩溃,它已提交的所有事务都已传输到至少一个副本。
与异步复制相比,半同步复制提供了更高的数据完整性,因为当提交成功返回时,就知道数据至少存在于两个位置。在源收到来自所需数量的半同步副本的确认之前,事务将被搁置且不会提交。
与完全同步复制相比,半同步复制速度更快,因为它可以配置为平衡数据完整性要求(确认收到事务的副本数量,参数rpl_semi_sync_master_wait_for_slave_count控制)与提交速度。
MySQL支持半同步复制后,使用半同步复制打造无损切换平台成为了可能。初看起来似乎实现比较简单,主机下挂载多个半同步备机,设置至少1个半同步备机接受到日志就提交事务。理论很丰满,现实很骨感,在实践中,会发现非常多的坑,架构要进行多轮迭代优化才最终实现目标。
下面我们来详细看下迭代情况:
部署:
MySQL参数设置:
金融数据库一致性>可用性
。AFTER_COMMIT
,发出事务的客户端只有在服务器提交到存储引擎并收到副本确认后才能获得返回状态。但是,在提交之后和副本确认之前,其他客户端可以在提交发出事务客户端之前看到已提交的事务
。说明:从 MySQL 8.0.26 开始,提供了新版本的源和副本插件, 在系统变量和状态变量中将术语“ master ”和“ slave ”替换为 “ source ”和“ replica ” 。如果安装新的 rpl_semi_sync_source
和 rpl_semi_sync_replica
插件,新的系统变量和状态变量可用,但旧的不可用。如果安装旧版本 rpl_semi_sync_master
和 rpl_semi_sync_slave
插件,旧系统变量和状态变量可用,但新变量不可用。您不能在实例上同时安装相关插件的新版本和旧版本。
切换条件:
说明:每个数据库实例上都会安装agent,由agent探测和上报主机(MySQL)心跳
补数:
存在问题:
AFTER_COMMIT
,发出事务的客户端只有在服务器提交到存储引擎并收到副本确认后才能获得返回状态。但是,在提交之后和副本确认之前,其他客户端可以在提交客户端之前看到已提交的事务。
如果出现问题导致副本无法处理事务,那么在源意外退出并故障转移到副本的情况下,此类客户端可能会看到相对于他们在源上看到的数据丢失。
通逧的说,当网络故障导致3台半同步备机都收到不主机日志,因为rpl_semi_sync_master_timeout设置为无穷大,半同步复制不会降级为异步复制,此时主机写入会卡住,客户端不会收到事务成功的返回直到超时,但此事务可能已经被其它客户端看到。假设卡主一段时间后,主机当机,此时卡住的事务都没有被同步到半同步备机上,但可能已经被客户端看到,产生了脏读。
部署优化:
参数优化:
解决问题: after_commit导致脏读问题
AFTER_SYNC
,所有客户端都会同时看到已提交的事务,这是在副本已确认接受到日志并返回信息到源上的存储引擎之后。因此,所有客户端都会在源上看到相同的数据。
如果源发生故障,在源上提交的所有事务都已复制到至少1个副本(保存到其中继日志)。源的意外退出和故障转移到副本是无损的,因为至少1个副本是最新的。新问题:
部署优化
解决问题:补数问题
切换条件
新问题
如图所示,当出现AZ1机房级故障的时候,主机上报心跳给Manager失败,同时AZ1的异步备机访问主机失败,导致DB切换(符合切换条件2),但实际主机和主机所在机房都是正常的,发生了误切。
源IP,源端口,网络协议,目标IP,目标端口
这个5元组中源端口会变化,所以出现一会通,一会不通的情况。
当出现AZ2半网断的时候,备机到主机时通时不通,此时主机上报心跳到Manager也会一会成功,一会失败,此时主机是否切换成为了一个随机事件,如果网络长时间没有恢复,可能部分DB会一直没有切换,而上层服务访问主机也是时通时不通,此时属于应该切换没有切。
切换条件
解决问题:半网断问题
新问题
切换条件
新问题
部署
解决问题: 异步备机脏读
新问题
切换条件
解决问题:主机半死不活
抗二次打击
如上图所示,AZ2网络故障,主机切换到AZ1,此时架构为1主1半同步备。如果AZ1故障,不会再切换AZ3(对于金融级数据库,单点不对外提供服务),如果AZ3故障,则会导致主机事务提交卡住。一句话来说,如果3AZ中的2个AZ先后故障,无法切换!
为了解决这个问题,引入第4个AZ,此AZ可以取同省邻近市的机房部署,同时设置rpl_semi_sync_master_wait_for_slave_count=2,则即使原3AZ中的2个AZ先后故障,还剩1主1半同备,仍然可以进行切换后对外提供服务。
跨城容灾
上面的架构虽然解决了同城二次打击问题,但没有解决遇到整个同城故障的问题,此时AZ1、AZ2、AZ3先后故障,无法切换。
为了解决跨城容灾问题,需要由4AZ架构改为3地5AZ架构,如上图所示,其中AZ3和AZ1,AZ2为同省不同城市,假如AZ2与AZ3距离为200公里,则写入耗时会增加7ms。这里的问题是如果AZ3机房故障,则写入耗时会增加到30ms以上!这也是为什么AZ3要选择同省就近的城市。
公众号"数据库之巅"记录了我在互联网金融数据库运维中走过的路和踩过的坑,感兴趣的同学可以关注。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。