阿里妹导读
MySQL数据库从诞生以来就以其简单、易用、开源为其主打特点,成为不少开发者首选的数据库系统。阿里在2008年开始提出"去IOE"的口号,其中,使用大量的MySQL,配合业务的改造替代原有的商业版Oracle系统。自此集团迈入了MySQL数据库的时代。根据阿里交易型应用的特点,以及双十一这样业界罕有的需求推动下,我们在官方的MySQL基础上增加了非常多实用的功能、性能补丁,打造了AliSQL这个业界响当当的MySQL分支品牌。
时间很快走到2014年,随着业务高速的增长,同城主备AliSQL部署的方式已经无法满足阿里对可扩展的部署、国际化、以及容灾方面的需求。“异地多活”成为了公司应用的新标准。“异地多活”也给底层的数据库提出了新的容灾要求。传统的Master-Slave架构下,主备如果不使用强同步模式就会存在数据丢失的可能,然而强同步下一旦有节点异常则整体不可服务。而且这套架构下需要HA工具来进行选主的仲裁和控制。
过去阿里巴巴的DBA们开发了高效可靠的HA以及一整套工具和流程来做主备切换后数据和日志的校验和订正。然而我们相信技术的发展能带来更大的运维便利性以及更好的用户体验。以Google Spanner以及Amazon Aruora 为代表的NewSQL系统为数据库的数据一致性给出了与以往不同的思路:基于一致性协议搭建分布式的多副本数据库。
本文字数近万字,建议对数据库感兴趣的童鞋收藏细看。
AliSQLX-Cluster
介绍
AliSQL X-Cluster(本文中简称X-Cluster)是阿里巴巴数据库团队推出的兼容MySQL 5.7,提供数据强一致功能,支持全球部署的分布式数据库集群产品。
说到AliSQL X-Cluster就不能不提其分布式核心,一致性协议。
X-Paxos是阿里巴巴自主研发的一致性协议库,目标是填补市面上高性能、易接入的一致性协议库的空白。而市面上已开源的一致性协议实现,包括etcd以及其他厂商等都存在或性能不够,或功能上无法满足复杂的现实应用场景需求的问题。
有了X-Paxos,可基于它打造一套强一致的分布式系统,X-Cluster是第一个接入X-Paxos生态的重要产品,利用了X-Paxos实现了自动选主,日志同步,集群内数据强一致,在线集群配置变更等功能。同时X-Cluster基于MySQL生态,兼容最新版本的MySQL 5.7,集成了AliSQL过去的各种功能增强。 MySQL的用户可以零成本迁移到X-Cluster上。
整体架构
先来看一下
X-Cluster
的基本架构:
上图展示的是一个部署三个实例的
Cluster
集群。
X-Cluster
是一个单点写入,多点可读的集群系统。在同一时刻,整个集群中至多会有一个
Leader
节点来承担数据写入的任务。相比多点写入,单点写入不需要处理数据集冲突的问题,可以达到更好的性能与吞吐率。
X-Cluster
的每个实例都是一个单进程的系统,
X-Paxos
被深度整合到了数据库内核之中,替换了原有的复制模块。集群节点之间的增量数据同步完全是通过
X-Paxos
来自驱动,如何复制,从哪个点回放不再需要运维人员或者外部工具来介入。
X-Cluster
为了追求最高的性能,利用
MySQL
的
Binlog
进行深度改造和定制来作为
X-Paxos
的
Consensus
日志,实现了一系列的
X-Paxos
日志接口,赋予
X-Paxos
直接操纵
MySQL
日志的能力。
为了保证集群数据的一致性以及全球部署的能力,在事务提交、日志复制回放以及恢复上
X-Cluster
都进行了重新设计。
事务提交、复制流程
X-Cluster的复制流程是基于X-Paxos驱动Consensus日志进行复制。
Leader
节点在事务
prepare
阶段会将事务产生的日志收集起来,传递给
X-Paxos
协议层后进入等待。
X-Paxos
协议层会将
Consensus
日志高效地转发给集群内其他节点。当日志在超过集群半数实例上落盘后
X-Paxos
会通知事务可以进入提交步骤。否则如果期间发生
Leader
变更,期间
prepare
的事务会根据
Paxos
日志的状态进行相应的回滚操作。相比原生
MySQL
,日志传输采用了多线程、异步化、
Batching
、
Pipelining
等优化手段,特别是在长传链路的场景中,效率提升明显。
Follower
节点也使用
X-Paxos
进行日志的管理操作,为提升接收效率,收到
Leader
传递过来的日志以后将日志内容
Append
到
Consensus Log
末尾,
Leader
会异步的将达成多数派的日志的消息发送给
Follower
,
Follower
的协调者线程会负责读取达成多数派的日志并加以解析,并传递给各个回放工作线程进行并发的数据更新。
Follower
的并发回放可以有多种方式,包括按照
Leader
上的
Group Commit
维度或者是按照表级别的维度,未来会引入最新的
writeset
方式来精确控制最大并发。
Failover
X-Cluster
只要半数以上的节点存活就能保证集群正常对外服务。因此当少数的
follower
节点故障时并不影响集群的服务能力。当
Leader
节点故障时会自动触发集群的重新选主流程。选主流程由
X-Paxos
驱动,在
Paxos
协议的基础上,结合优先级推选出新的
Leader
节点。
对于
X-Cluster
而言,
failover_time = election_time+ apply_time
election_time
代表协议选主的时间,一般在
10s
左右,
apply_time
是数据应用日志的时间,取决于回放的速度,优秀的并行回放算法能把应用日志的时间控制在
10s
之内。
相对来说之下
Leader
节点故障是一个相对复杂的场景,故障包括了实例崩溃、服务器宕机、网络隔离等等。
如上图所示,一个三节点的
X-Cluster
集群,左边的
Case
是原
Leader A
节点宕机,因此
B
节点和
C
节点会在较长的时间内收不到
Leader
的心跳,因此在一个选举超时周期后,
B
节点开始尝试推选自己为
Leader
,并且
C
节点同意,那么
B
成为新的主节点,恢复服务能力。
右边的
Case
是原
Leader A
节点发生网络分区,此时在选举超时后,
BC
两个节点的行为和之间的
Case
类似。
A
节点由于无法将心跳和日志发送给
BC
两个节点在超时后会降级,然后不断尝试选自己为主,但是因为没有其他节点的同意,达不成多数派,一直都不会成功。当网络分区恢复后,
A
收到
B
节点的心跳,触发
Leader stickness
机制,
A
自动加回集群。
AliSQLX-Cluster
的优化特性
X-Cluster
拥有一系列的新功能和特性,以满足不同类型的应用对于功能和性能上的需求。
跨地域部署
X-Cluster
的一个显著功能就是能够支持跨地域部署,在跨域的情况下也能保证集群数据强一致,拥有城市级容灾的能力。即使某个城市的机房全部宕机,只要保证集群多数派节点存活,所有的数据都不会丢失。依赖
X-Paxos
以及数据库内核中异步化工作线程的改造,在数十毫秒的网络
RTT
(
Round Trip Time
)下,
X-Cluster
依然有非常高的吞吐性能。拥有了跨地域部署的能力
,X-Cluster
的部署方式是非常灵活的。业务可以根据实际的业务情况以及不同的容灾级别需求,选择同机房容灾,机房容灾,城市容灾部署,并且可以在不同的容灾级别之间灵活切换。
集群的动态配置和选举
X-Cluster
支持在线集群配置变更。包括:
-
增加节点下线节点
-
修改节点类型为一致性节点或者是只读节点
-
修改节点优先级
-
修改集群的
Leader
-
修改只读节点复制源
所有的上述修改配置的过程在线进行,不会阻塞业务的正常运行,集群配置的变化也会严格按照
Paxos
协议进行,记录日志并且对应的推动状态机变更,并且有完善的恢复机制。保证最终集群内配置达成一致,不会因为集群配置变更过程中的异常导致脑裂或者其他配置出现终态不一致的问题。
X-Cluster
集群的节点从功能上看有两个类型,包括参与选主与多数派协议的一致性协议节点还有只读节点。一致性协议节点是
X-Cluster
的基础。目前一个
X-Cluster
集群支持至少
1
个一致性节点,至多
99
个一致性节点。只读节点可以无限扩展。用户可以从一开始的单节点集群开始,后续不断根据需求扩展不同类型的节点。
优先级
应用往往对于容灾后新主节点是有要求的,在原先的主节点意外宕机后,新主如若落在了一个低规格的节点,那么对于应用来说是很难接受的服务降级。
X-Cluster
支持同一个集群中的节点拥有不同的优先级,用户可以根据实际的部署需要,在配置集群时为每个实例节点设置优先级。
优先级功能主要包括以下两方面:
权重化选主代表选主的顺序权重。只要在选举的时候没有网络隔离,选举
Leader
的顺序会按照集群存活节点的权重顺序进行。权重越高的节点,就有更大的优先级被选为
Leader
。我们实现了两段式的选举时间,第一阶段是集群内统一的租约时间,而二阶段是根据权重来决定,权重越大的节点时间越短,也就是会越早发起自选举。除此之外,还有一重权重检测机制来保证权重优先级,节点上任时会广播检测一遍所有能够联通的集群内节点,如果发现权重更高的节点会主动发起一次
Leader Transfer
将
Leader
角色过继过去。
权重化选主在跨地域部署的场景下尤其重要。跨地域的部署业务和数据库之间的访问延时会非常敏感,如果
Leader
节点随机的切换到了另一个地域的机房可能会导致应用大规模的访问异地实例,大幅增加客户端的响应时间。权重化选主可以完美解决此问题。按照应用的部署需求进行动态设置,非常灵活。
策略化多数派是指在事务提交日志同步过程中,哪些节点必须要日志复制完成。复制优先级分为两档,强复制和弱复制。标准的
Paxos
只要超过半数的节点同步日志即可推进状态机,但是由于每个连接会自动分配路由的问题,可能在跨
Region
的访问中
RTT
时间会有误差。
那么为了缩短网络
/
节点故障后按照选主优先级重新选主并继续服务的时间间隔,我们可以配置在规定日志复制到多数节点的基础上必须还要复制到了所有强复制的节点才可以推进状态机并返回客户端事务提交成功的响应。这是一个类
Max protection
模式的设计,如果检测到强一致节点宕机,可选择适当的降级。
低成本副本管理
在
X-Cluster
中,副本按照是否有数据状态机分为普通型
(Normal)
,日志型
(Log)
两类。普通型拥有全部的功能;日志型只拥有日志不包含数据。如果日志型节点还是一个参与
Paxos
投票的一致性节点,那么它只有投票权,没有被选举权。
之所以要创建不同的类型的副本,还是出于应用需求以及成本控制的考虑。相比传统的两节点主备复制,
X-Cluster
的常规同城部署方式是三节点。
日志型副本是作为降低部署成本的一种选择。日志型副本只存储日志,不需要存储数据,也不需要回放日志更新数据。因此无论是存储还是
CPU
的开销,日志型副本相比普通副本都有很大的优势。在实际应用规划中,非常适合来当作容灾型的节点部署。
只读节点管理
如上图所示,
X-Cluster
集群中的只读节点可以从任何一个一致性节点复制日志,这不仅是考虑到如果所有节点的日志都从
Leader
节点复制会对
Leader
节点造成过大的网络和
IO
瓶颈,而且由于跨区域部署下不同地域的数据状态机可能会有延时,设置了读写分离的用户在特定的场景下需要有特定的只读状态延时的要求。但是这时的问题就是如果某个一致性节点发生了宕机,那么和它建立复制关系的只读节点应该如何进行灾备联动?
作为一个自运维的数据库服务,
X-Cluster
自然要解决好这个问题。
X-Cluster
定义了
Learner Source Group
。每个
Group
是一个只读节点的容灾单元。当
Group
内某个一致性节点发生意外状况(宕机或者网络隔离)集群会根据
Group
的配置,将挂载在故障节点下的只读节点配置到
Group
中另外一个正常工作的节点下进行数据同步。
高性能日志
MySQL
系统在开启主备复制的情况下除了会记录
Binlog
之外,在备库上还会记录一份
RelayLog
。即从库通过指定对应主库的
Binlog
位置去同步一份二进制日志写入
RelayLog
,供复制线程读取和回放。在
X-Cluster
中,只使用一份日志进行节点间的同步,利用
X-Paxos
的插件式日志模块的特性,每个节点有一份
Consensus
日志。
这样既方便了对
Consensus
日志的管理,也降低了日志存储以及读写的开销。
Consensus Log
拥有
Append
,
Get
,
Truncate
以及
Purge
等基本接口,它的控制权完整地交给了
X-Paxos
,由
X-Paxos
进行控制。除此之外,
X-Cluster
为
Consensus Log
设计了日志索引和日志缓存、预读机制,极大的提升了日志模块的性能保证一致性协议运作的高效性。
异步化事务提交
传统的
MySQL
都是
One Thread per Connection
的工作模式,在引入线程池后是以一个线程池孵化一定数量的工作线程,每个线程负责处理一次
query
的解析、优化、修改数据、提交、回网络包等等。集群需要跨地域部署下,一次事务的提交由于需要在集群之间同步事务日志,受限于网络的
RTT
的限制,会达到数十毫秒的级别,那么对于一个普通的写事务来说,大量的时间会耗费在同步节点日志等待事务提交的过程。
在大压力下,很快数据库内部的工作线程会耗尽,吞吐达到瓶颈。如果一味的放大数据库内部的工作线程数目,那么线程上下文的代价会大幅增加。如果将整个事务的提交异步化,将工作线程从等待
X-Paxos
日志同步中解放出来,去处理新的连接请求,在大负载下可以拥有更高的处理能力。
异步化改造的说明示意如下图所示:
X-Cluster
的异步化提交核心思想是将每个事务的请求分成两个阶段,提交之前一个阶段,提交和回包一个阶段。两个阶段都可以由不同的工作线程来完成。为了完成异步化的改造,
X-Cluster
增加了等待同步队列和等待提交队列,用于存储处于不同阶段的事务。前者队列中的事务是等待
Paxos
多数派日志同步的事务,后者是等待提交的事务。当用户发起
Commit/Rollback/XA_Prepare
时,处理用户连接的线程池
Worker
产生事务日志并将事务上下文存储到等待同步的队列中。等待同步队列的消费由少量数目的
worker
线程来完成,其余工作线程可以直接参与其他任务的处理。
事务等待多数派完成后会被推入等待提交队列。这个队列里的事务都是可以被立即提交的。所有的
worker
线程发现该队列里有事务,就可以顺道取出来执行提交操作。
这样一来,系统中只有少数的线程在等待日志同步操作,其余的线程可以充分利用
CPU
处理客户端的请求。