TDSQL简介
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
TDSQL(Titan Distribute SQL) —一种分布式数据库之容灾篇
一、传统的分库分表及由此引入的问题
由于业务数据量巨大以至于无法单表存储,于是,我们习惯了分库分表的方式。
最常用的莫过于按照QQ号的后三位分1000表,除此之外,还有按照大区分表,按照时间分表,等等。
于是我们习惯了下面这样一种SVR与DB交互的架构结构。
图1—传统的分库分表存储
这个我们习以为常的数据库存储结构,其实包含了一些问题,本篇将主要讨论主备一致性切换问题,并给出TDSQL主备切换的方式。
问题如下:
1、主备的异步同步,在主机宕机的情况下,无法保证数据已经同步到了备
机。
2、人工切换,则对DBA同学的实时处理提出非常高的要求。
3、自动切换,可能出现不同SVR对于主DB的健康状态判断不一致,造成
不同SVR把数据写入到不同DB的情况即——脑裂。
4、即使通过仲裁节点来统一调度SVR连向主DB或者备DB,如果流程处
理的不好,也可能因为SVR感知切换的时间差在短时间内造成脑裂。
如何解决上面的问题,业界给出了很多的方案,例如国外有Galera这种通过协议插件来实现一致性的方案(但这种方案在跨IDC时的性能非常差),国内也有阿里RDS,TDDL,360的atlas中间件,但上述的方案要么在主备切换的一致性,要么在主动切换,要么在性能上都会有或多或少的问题,因此我们在参考上述方案的基础上实现了今天要给大家介绍的方案Titan Distribute SQL —TDSQL。
二、TDSQL主备切换方案
2.1 TDSQL容灾架构
图二、TDSQL容灾架构
图二是一个大家熟悉的具备调度能力的分布式集群,下面分别来介绍一个各个模块的作用及如何互动。
DB——图中的绿色部分,是集群的核心部分,也就是mysql节点。
为了实现主备的强一致性和较高的性能,这里必须使用我们在Mariadb 基础上进行二次开发的MySQL。
注意,只有主DB提供写服务,其它DB会被Agent自动设置成只读。
Agent ——监控模块,Agent监控MySQL节点的健康状况,并上报心跳给Scheduler。
Node ——每个MySQL进程和Agent要一起部署,二者组合在一起得到一个最小的存储控制单元Node。
SET——为了实现容灾,通常使用一主N备个节点来组成一个最小的容灾存储单元,主Node和备Node存储的数据一致,通过后面介绍的强同步来同步数据。
为了实现跨IDC容灾,不同的Node节点需要部署在不同的IDC。
Scheduler ——调度模块,Scheduler接收Agent的请求,并判断该DB 是否宕机,如果宕机时间超过阀值,则会触发容灾流程。
Proxy——业务请求分发模块,其实是一个MySQL代理,适配MySQL协议,并把请求发送到后端的。
Zookeeper ——一个开源的一致性存储集群,在TDSQL架构中,所有控制节点通过Zookeeper进行交互。
2.2 TDSQL各个控制模块的容灾
1、Agent —Agent挂掉存在两种情况:
1、整个物理机器出现类似掉电的情况,在这种情况下是要做Node节
点的主备切换的,这个时候Agent在Zookeeper创建的心跳节点(一个zookeeper的临时节点)将会消失,Scheduler监控到心跳节点消失,在超过阀值时间后,将会出发一个容灾流程。
2、Agent本身挂掉,Agent启动的时候会有一个监控进程监控工作进
程是否活着,如果发现Agent进程非正常退出,会主动把Agent拉起,而从Agent挂掉到拉起的时间足够短,因此不会造成Scheduler触发主备切换流程。
2、Scheduler——多个Scheduler通过Zookeeper进行选举,保证任何时刻都有且只有一个Scheduler在提供服务。
3、Proxy ——可以使用多种方式容灾,例如使用LVS、使用MYSQL自带的DNS,或者给业务端一个ip列表让其自动轮询。
2.3 主备数据复制
2.3.1 传统的异步复制:
传统的MySQL异步复制,是把主机(这里的主机是指主MySQL进程,本节其它地方的主机与此解释相同)Binlog中的事务异步的发给备机。
主机并不等备机的应答。
这就存在一个主备延迟的问题。
通常我们可以通过在备机执行Show Slave Status来查看主备的延迟。
在主备延迟比较大的情况下,直接进行主备切换会造成大量的脏数据。
2.3.2 半同步复制
MySQL5.5引入了半同步复制。
所谓的半同步复制,是指主机不仅要把同步事务发给备机,而且要等待备机的应答。
由于备机只是把同步事务写入Relay Log就返回主机应答,而不需等要把Relay Log中的事务加载到引擎中后再返回,因此备机的返回给主机的应答会很快。
而主机必须等待备机的应答之后,才返回给用户应答(如果有多台备机,只要有一个备机给与应答就可以返回前端用户了)。
这就保证了主备的一致。
图三—MySQL半同步复制流程[1]
上述流程保证了主备数据的一致(对于备机返回应答,主机没有来得及返回前端响应就宕机的情况下,参考下面的注释),但如果备机一直不给主机(例如网络丢包)应答,则主机的事务处理会被卡住,MySQL利用
rpl_semi_sync_master_timeout来设置了这个超时时间,当应答时间超过此阀值时,主备同步会自动变成异步复制。
注:
数据库操作中有一个原则:
1、如果返回给客户端的是成功的应答,则证明此事务已经落盘。
2、如果返回的是失败的应答,则证明操作对数据库不会有任何影响。
3、如果没有返回前端应答,则操作可能成功也可能失败。
在实际应用中,
这种情况需要的是二次核对。
2.3.3 强同步复制
2.3.2中主机长时间收不到备机应答的情况下会变成异步复制的机制,对于普通的应用是没有关系的,但是对于一致性要求很高的情况下,这种机制是不能被允许的。
同时,在原来的机制上,主备同步需要处理线程做同步等待,也就是说如果有1w笔并发,就需要1w个处理线程在等待,这造成了性能的严重下降。
因此,我们在半同步复制的基础上做了修改,关闭了半同步向异步同步切换的过程,同时把主备同步事务时的同步等待机制改成异步处理,这样就可以使用一个异步线程池来完成高并发。
为了不影响和业务请求和宕机情况,在强同步复制的SET中,最小配置是一主两备。
2.3.4 全局事物标示符号(Global Transactions Identifier——GTID)
GTID代表一个数据库事务, MySQL 5.6 版本增加了GTID2来强化数据库的主备一致性,故障恢复以及容错能力。
GTID的官方定义是:GTID = source_id:transaction_id,source_id 代表执行实物的主库uuid(server_uuid), transaction_id 是一个从1开始的自增计数,表示在这个主库上执行的第n个事务。
MySQL会保证事务与GTID之间的1:1映射。
在TDSQL切换后重建主备关系,而同步点就是通过GTID给出的。
我们假设主机的Server_uuid 为M,GTID为M:123456,同理备机S1的GTID为M:123455,备机S2的GTID为M:123456,在切换的时候,由于S2的同步点是最新的,所以新建的同步关系是S1 为备机,S2为主机,S1只要把M:123455发给S2即可建立新的同步点,而发生在S2上的新事物,从S2:1开始数序向下写。
2.4 主备一致性切换流程
经过上面的几个章节,现在我们来描述主备一致性切换流程,参见图一:
1、当Scheduler监控Master的Agent上报心跳为宕机状态,或者Master
的Agent维护的zookeeper 心跳节点消失,并且这段时间超过阀值时,
则出发一个切换流程,进入步骤2。
2、Scheduler会判断备机是否满足切换要求,判断条件如下
a)至少有两个备机节点是健康的。
b)备机和主机节点的延迟在阀值以内。
c)备机的GTID是有效的。
如果满足上述切换条件,选出可以参与本次选举主节点的备节点列表(后续简写为参选列表),则进入3,否则切换流程失败。
3、Scheduler判断主节点宕机类型,如果是zookeeper中的心跳节点存
在,但是心跳节点中的信息反映为主DB宕机,则下发降级主DB命令,进入4;如果是主节点Agent在zookeeper中维护的节点不存在(这种情况一般是主节点所在的物理机器出现问题,例如掉电)进入步骤5。
4、主节点Agent接收到降级的信息后,会把主DB设置为只读模式,并安
全杀死所有连接,并返回给Scheduler降级主DB完成。
5、Scheduler向参选列表中的所有备机发送加载relay log的命令,进入
6.
6、参选列表节点Agent 加载relay log,完成后上报同步点GTID给
Scheduler。
7、Scheduler在收到所有参选列表节点的应答后,选出具有最大GTID的
节点作为新的主节点。
并下发新主节点给所有参选列表中的节点。
8、参选列表节点Agent重建主备同步关系,设置同步主DB为Scheduler
新选出来的主节点,被选为主节点的Agent会把DB设置成读写模式提供读写服务,然后应答Scheduler。
9、Scheduler修改SET中的主备关系。
10、Proxy监控主备关系zookeeper节点,感知到了主备关系的变化,把
后续的请求发送给新的主节点。
以上就完成了一次主备切换。
三、总结
强一致性的切换需要两个条件:
1、主备数据的一致性保证,这个通过我们修改的强同步复制实现。
2、主备切换流程的合理,这里的主要理论依据是,任何时候不能在一个SET
中出现两个主节点。
这个我们通过2.4中的流程进行了保证。
综上,我们基本实现了一个强一致性容灾的机制,当然这里也会有些问题,例如主备切换时没有应答的客户端请求其实是无法判断最终结果的,这个需要在业务侧对账或者在更上层来完善这个主备切换的机制,这些问题我们会继续关注并解决。