数据库高可用(High Availability,HA)是指在系统遇到故障或异常情况时,能够自动快速地恢复并保持服务可用性的能力。如果数据库只有一个实例,该实例所在的服务器一旦发生故障,那就很难在短时间内恢复服务。长时间的服务中断会造成很大的损失,因此数据库高可用一般通过多实例副本冗余实现,如果一个实例发生故障,则可以将业务转移到另一个实例,快速恢复服务。
常见的高可用架构有两种,一种是主备架构,另一个是多活架构。

主备架构中,有一个实例是主库,其他实例是备库。主库发送日志给备库,备库通过回放日志来同步主库的数据,一旦主库发生故障,可以选择其中一个备库升主,接管业务。主备架构根据日志类型分两种,物理主备和逻辑主备。
多活架构有基于共享存储的共享集群,比如Oracle RAC,也有基于逻辑日志的多活架构,比如MySQL的双主多活。
YashanDB目前支持物理主备和共享集群两种高可用方案,本文将聚焦于物理主备的高可用介绍。
主备高可用方案挑战
主备高可用方案的挑战通常有以下几点:
YashanDB主备针对上述挑战,做了深度优化,使得主备同步性能和自动切换能力达到极致。下面将介绍YashanDB高性能主备复制,以及自动故障转移的技术解析。

YashanDB的物理主备架构如上图所示,左边是主库的组件,右边是备库的组件。
各组件解释如下:
YashanDB支持一主多备部署,最多可达32个备库。备库可以在线添加,并且一次可以并行添加多个备库。在并行创建备库的过程中,主库只从磁盘读取一次数据,然后将数据转发给多个备库。
YashanDB还支持级联备库,即备库可以从另一个备库接收Redo日志,无需和主库建立连接。级联备可以减少主库的压力,可用于异地容灾。YashanDB的每个备库可以连接多个级联备库。

YashanDB主备对数据的保护是基于事务级别的。一个事务提交前,如果这个事务的日志已经在备库落盘,那么该事务就不会在主库故障后丢失。
为方便用户对数据的保护程度和可用性进行权衡配置,YashanDB提供3种不同的保护模式:
YashanDB在日志发送方面,做了很多优化,来提高日志发送的性能:

备库停机一段时间后,会落后主库较多Redo文件。
YashanDB的备库重连后,直接从主库日志缓存接收最新Redo,其余未接收到的Redo,将通过归档日志拉取线程补齐,这部分归档称为归档GAP。
由于最新Redo是通过日志缓存发送的,所以不会产生额外的Redo磁盘IO,对主库性能影响较小,同时归档拉取线程逐个获取主库归档,可加速备库追赶主库。

在最大保护模式下,主库的Redo日志没有被备库接收前,这部分日志对应的事务提交将阻塞。如果此时主库宕机,备库升主,新主库不包含这部分日志,旧主库降备后,需要将这部分日志回退,这部分日志称为未决日志。
未决日志可能会回退,其对应的事务不可提交,相应的脏页不能刷盘(如果脏页刷盘后,日志被回退了,就会违反WAL原则)。
未决日志不能被备库回放,因为主库上可能没有提交对应的事务。
如果日志被主库和所有同步备持久化,则这部分日志不会被回退,这个日志点称为commit index。主库维护commit index识别未决日志,同时主库会将commit index同步给备库。

上图场景中,主备处于最大保护模式,备库还没有收到主库的log 4和log 5这两条日志。虽然这两条Redo日志在主库落盘了,但是如果主库此时发生故障,备库升主,旧主库降备后,这两条日志将会被回退。

上图场景中,主库的log 4这条日志已经写入主库的日志缓存,并且被发送到备库,但是主库还没有将log 4落盘。如果主库此时发生宕机重启,主库上不会包含log 4这条日志,备库上这条日志将会被回退。
YashanDB的备库支持并行回放,来提高备库回放Redo的性能。
备库通过一个协调线程读取和分析Redo日志,然后将Redo按照会话ID哈希分配给后台回放线程,让这些后台线程并行回放Redo。
并行回放大大提高了回放速率,使得备库Redo不积累,备库查询延迟低,并且故障切换的RTO很低。
YashanDB的备库并行回放,复现了主库的session执行顺序,可以保证事务瞬时一致性,解决备库读一致性和回放性能难以兼顾的痛点问题。即使主库在高并发业务下,备库查询延迟也可以做到很低。

当主库发生故障时,需要将某个备库切换为主库,并将业务转移到新的主库上。除了手动执行故障切换,YashanDB还支持自选主、仲裁切换两种方式的自动故障切换。
YashanDB的自选主切换是基于Raft算法实现的。Raft是一种共识算法,能确保节点故障或网络分区下保持强一致性。
主备通过心跳检测对方是否存活,主库定期会给所有备库发送心跳,如果备库在超时时间内没有主库的心跳,那么备库就认为主库发生了故障。主库事务提交,要等待多数派节点将Redo落盘,除了主库节点,至少要有节点总数/2(下取整)个备库收到主库Redo 。主库故障,备库心跳超时后,会发起选举投票,如果收到多数派节点的投票,则升为新的主库。投票算法可保证,新主是多数派里Redo最多的,而事务提交要等待多数派落盘,因此新主库一定落盘了所有已提交事务的Redo,不会丢数据。
Raft的每次选举都会产生一个新的任期(term),在一个任期内,最多只有一个主库。
Raft将系统中的角色分为Leader、Follower和Candidate。工程上为了提高系统稳定性,加入了PreCandidate角色:

如上图所示,数据库启动后都是Follower角色,如果没有收到Leader的心跳,到达超时时间后,会发起预选举。预选举成功后,会增加自己的任期然后发起正式选举,正式选举成功后,就成为Leader,即从备库升为主库。在选举过程中,如果遇到任期比自己高的节点,或者发现当前任期的Leader,则会中断选举,回到Follower角色。
Raft至少需要3个节点,不适合一主一备的自动切换,为此YashanDB提供了基于外部组件的一主一备仲裁切换功能。
yasom是YashanDB的轻量级的数据库管理进程,一般部署在非数据库服务器,可对主备状态进行监控。yasom和数据库之间有个yasagent进程进行代理通信,yasagent部署在数据库所在服务器。
yasom检测到主库异常后,在满足切换条件的情况下,给备库下发failover命令进行切换。切换条件为yasom和备库都感知不到主库,如果其中任一个能感知到主库,说明主库正常。

如上图3种情况,情况1表示主库所在服务器故障,yasom和备库都感知不到主库,可以切换。情况2表示主库进程故障宕机,yasom和备库都感知不到主库,可以切换。情况3表示yasom和主库所在服务器网络异常,但是主库和备库之间网络正常,此时不会切换。
零丢失模式
零丢失模式下,只有确定备库不丢失数据的情况下,才可以升主,牺牲一定的可用性,但是不会丢失数据。
脑裂自动修复
非零丢失模式下,旧主库和新主库的数据可能不一致,旧主库降备后,与新主库发生脑裂。
yasom发现备库脑裂后,将下发脑裂修复命令,修复备库不一致的数据,使主备复制恢复正常。
首先找到主备的日志分歧点,然后扫描备库Redo得到分歧点之后修改过的页面,将这些页面的ID发给主库,从主库获取对应页面并覆盖备库本地文件。再以分歧点lsn为基线,将主库上大于该lsn的数据页面发送到备库覆盖,最后将主库分歧点之后的Redo发送到备库,进行回放。回放完成后,主备将正常同步Redo,脑裂修复完成。

在客户实际运用数据库的过程中,性能与业务连续性无疑是最为重要的两大考量因素。尽管主备高可用方案在业界极为普遍,但在真实业务场景中仍面临多重挑战。我们在架构技术与编程实现等方面进行了深入的优化工作,以提升主备部署模式的同步性能及高可用保障。接下来,我们会继续丰富功能和性能,还将在以下几个方面继续打磨和增强:
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。