2.8 集群同步协调&状态管理

2.8.1 概述

大多数时候,dble结点是无状态的,所以可以用常用的高可用/负载均衡软件来接入各个结点。
这里不讨论各个负载均衡软件的使用。
主要讨论一下某些情况下需要同步状态的操作和细节。
注:本部分内容需要额外部署zookeeper用于管理集群的状态和同步。

2.8.2 相关配置

2.8.2.1 cluster.conf

以ZK为例

# 开启集群模式
clusterEnable=true   
# 集群模式元数据中心为zk
clusterMode=zk   
# zk地址
clusterIP=10.186.19.aa:2281,10.186.60.bb:2281
#zk为dble提供的根目录
rootPath=/dble  
#本组dble的组名
clusterId=cluster-1 
# 是否需要同步Ha状态 
#needSyncHa=false  
# 拉取一致性binlog线的超时时间
#showBinlogStatusTimeout=60000  
#自增序列类型
sequenceHandlerType=2  
#自增序列默认开始时间
#sequenceStartTime=2010-11-04 09:42:54 
#自增序列类型为3时,instanceId是否由ZK生成
#sequenceInstanceByZk=true

2.8.2.2 bootstrap.conf

instanceName 实例名称,用于发起集群任务或者汇报自己完成集群任务的标记 instanceId (自增序列使用,根据类型范围为0到1023 或者0到511)

2.8.3 初始化状态

方式1.

通过执行脚本init_zk_data.sh方式将某个结点的配置文件等数据写入ZK,所有结点启动时都从ZK拉取配置数据。

方式2.

在第一个结点第一次启动时自动将自己的配置文件写入ZK,其他结点启动时从ZK拉取。
第一个结点的判定:用分布式锁抢占的方式,未抢占到结点会阻塞等待直到获取到分布式锁,如果此时初始化标记已经被设置,则从ZK拉取配置,否则将自己本地配置写入。
初始化流程

2.8.4 状态同步

A.DDL

做DDL时候会在执行的某个节点成功后,将消息推给ZK,ZK负责通知其他结点做变更。流程如下图:

DDL流程 类似于二阶段按提交的方式

注:

  1. 新结点启动时,加载元数据前会检测是否有其他结点在做DDL变更,如果有,则等待。
    否则加分布式锁防止加载元数据期间其他结点做DDL变更, 直到元数据加载完释放分布式锁 。
  1. "等待其他节点执行完成子任务" 指的是:每个节点完成任务后,都会创建子节点用来告知分布式协调器自己已经完成,执行主任务的节点负责检查所有结点是否都创建了子节点(即检查是否都完成了任务),如果完成则删除父结点(回收资源)。此操作为原子操作。
  1. 发起结点故障:如果执行DDL的结点故障下线,其他结点会侦听到此消息,保证解开对应结点的tablemeta锁,并记录故障告警(如果配置了告警通道),需要运维人工介入修改ZK对应ddl结点的状态,检查各个结点meta数据状态,可能需要reload metadata。
  1. 逻辑上不应该有某个监听节点上加载meta失败的情况,如果发生了,告警处理

    (人工介入对应结点的meta是不正确的,需要reload meta)

  1. 注:view目前是异步模式,可能存在某个间隙view修改成功,查询仍旧拿到旧版的view结构。

B.reload @@config/ reload @@config_all

执行流程如下图:

reload流程

注:如果在部分结点失败,则会返回错误及错误原因以及结点名。 设计影响面:db.xml,sharding.xml, userxml ,sequence_conf.properties 和sequence_db_conf.properties

C.拉取一致性的binlog线

目的:

获得后端数据库实例的一致性binlog位置。由于两阶段提交的第二阶段执行在各结点无法保证时序性和同步性,所以直接下发show master status获取binlog可能会造成不一致。

实现方式:

如下图,当前端收到show @@binlog.status 语句时,遍历当前所有活动session查看状态 。
若session处于绿色区域,则在进入红色区域前等待知道show @@binlog.status结果返回
若存在session处于红色区域,则需要等待所有红色区域的session返回结果走出红色区域后下发show master status。

binlog流程

超时处理:

此处有可能有死锁发生。
场景:session1 正在更新tableA,处于绿色区域,session2下发有关于tableA 的DDL,等待 metaLock解锁,处于红色区域.session3 下发show @@binlog.status.
此时session1 等待session3, session2等待session1,session3等待session2.
因此引入超时机制。如果session3 等待超过showBinlogStatusTimeout(默认60s,可配置),自动放弃等待,环状锁解除。

集群协调:

1.收到请求后同步通知ZK,先等待本身结点准备工作结束,之后zk通知其他结点处理。
2.所有结点遍历各自的活动的session,进入红色区域的等待处理完成,绿色区域的暂停进入红色区域。
3.结点将准备好/超时将状态上报给ZK
4.主节点等待所有结点状态上报完成之后,判断是否可以执行任务,若是,则执行show @@binlog.status并返回结果,否则报告本次执行失败。
5.主节点通过ZK通知各结点继续之前的任务

集群超时处理:

若有结点超时未准备好,主节点会报超时错误,并通过ZK通知各结点继续之前的任务。

故障处理:

主节点执行过程中故障下线,其他结点会感知,保证自己结点一定时间后自动解锁继续原有任务。
ZK状态需要人工干预。人工等待所有结点超时之后,手动删除/修改zk上的状态信息以便下次执行时不出问题。

D.View管理

在使用集群模式是,使用zk进行view视图信息的管理,使得整个dble集群能够进行视图信息的同步 由于视图只用于数据查询且不会造成数据异常的属性,在视图同步时采用异步同步的方法

binlog流程

注 在zk上的view数据信息如下形式 key schema.table value {"serverId":"create_Server_id", "createSql":"view_create_sql"}

E.online状态

dble启动时候会在online的目录下注册自己的信息,如果此时正在做DDL,会阻塞一段时间,防止表结构元数据不一致。
功能:
1.作为集群协调时候检查哪些dble完成了对应任务的标准 2.dble故障后同集群的其他实例会发现状态并处理完收尾的状态,主要是DDL状态残留,拉取一致性binlog线或者暂停流量的残余。

F.高可用命令同步

当启用needSyncHa时候,此选项才生效

F.1 disable工作流程:

1.申请分布式锁
2.本地执行disable
3.将信息写入集群
4.等待所有结点完成disable
5.完成并清理
6.释放分布式锁

F.2 enable工作流程:

1.申请分布式锁
2.本地执行enable
3.将信息写入集群
4.释放分布式锁
5.其他订阅结点异步完成enable

F.3 switch工作流程:

1.申请分布式锁
2.本地执行switch
3.将信息写入集群
4.释放分布式锁
5.其他订阅结点异步完成switch

G.暂停流量(一般用于扩容迁移等)

G.1 pause流程
  1. 申请分布式锁 pause_node.lock
  2. 通知其他结点
  3. 本结点停流量
  4. 等待其他结点完成停流量 或者超时
  5. 返回暂停成功或者失败
  6. 释放分布式锁
G.2 resume流程
  1. 申请分布式锁 pause_node.lock
  2. 本结点恢复流量
  3. 通知其他结点
  4. 等待其他结点完成恢复流量 或者超时
  5. 返回暂停成功或者失败
  6. 释放分布式锁

2.8.5 XA日志管理

在使用集群模式时,未完成的XA日志会存放在zookeeper上,更加安全,防止某台机器硬盘物理损坏导致日志丢失(此处可能会有并发高吞吐引发的性能及其他问题,待测试)。

2.8.6 ZK整体目录结构

rootPath(配置在cluster.cnf中的)
    clusterId(配置在cluster.cnf中的)
        conf
            inited
            status
                operator
                    instanceName(临时的,key为bootstrap.cnf配置内容。功能:reload响应id)
            sharding(sharding.xml的json信息)
            db(db.xml的json信息)
            user(user.xml的json信息)
            migration(用于暂停流量)
                pause
                    instanceName(临时的,key为bootstrap.cnf配置内容。功能:响应结点)
                resume
                    instanceName(临时的,key为bootstrap.cnf配置内容。功能:响应结点)
            sequences
                instanceid //zk 分布式方式
                incr_sequence//批量步长方式
                    table_name
                common(sequence_conf.properties 或者sequence_db_conf.properties)
        binlog_pause
            status (发生时建立,结束后回收)
                instanceName(临时的,key为bootstrap.cnf配置内容。功能:响应结点)
        lock
            syncMeta.lock(临时的,启动时防止元数据变更)
            confInit.lock  
            confChange.lock
            binlogStatus.lock
            ddl_lock/schema.table
            view_lock/`schema`.`table`
            dbGroup_locks/groupName
            pause_node.lock
        online
            instanceName(临时的,key为bootstrap.cnf配置内容。)
                ddl
                    schema.table.PREPARE(ddl操作的第一阶段,用于加锁)
                        instanceName(临时的,key为bootstrap.cnf配置内容。功能:响应结点)
                    schema.table2.COMPLETE(ddl操作的第二阶段,此时 ddl 已执行完成,开始同步元数据)
                        instanceName(临时的,key为bootstrap.cnf配置内容。功能:响应结点)
                    schema.table3.PREPARE
        xalog 
            node1
            node2
        view
            schema:view         
            operator (订阅目录)
                schema.view:(update/delete,注意:create当作update处理)
                    instanceName(临时的,key为bootstrap.cnf配置内容。功能:响应结点)             

        dbGroups     
            dbGroup_status
                groupName
            dbGroup_response
                instanceName(临时的,key为bootstrap.cnf配置内容。功能:响应结点)

2.8.7 全局序列

类twitter snowflake 方式,ZK完成的工作是生成每个节点的instanceID。
类offset-step 方式,ZK完成的工作是存储当前的Step值。

2.8.9 移除集群中分布式锁的方式

前置知识

当clusterMode=ucore (使用爱可生商业集群调度中心), 使用ucore集群调度中心的分布式锁是有超时机制的;dble内部为了防止使用期间超时,内部会给分布式锁分别创建一个线程(renewThread),专门用来续约对应分布式锁,防止锁超时。
clusterMode=zk,zk集群调度中心的分布式锁没有超时机制,则不用创建renew线程。
可检索 续约日志:renew lock of session success

背景

在某些不可预测的情况下,可能会导致分布式锁残留,导致dble内部的renew线程一直工作,比如conf reload的场景;希望能通过不重启的方式实现renew线程自杀,最终达到分布式锁超时而被释放的效果。

查看renew线程列表

在管理端侧,查看dble_cluster_renew_thread表,此表展示当前时间点正在做分布式操作时获取分布式锁对应的renew线程列表,如:

mysql> select * from dble_cluster_renew_thread;
+-------------------------------------------------------------------+
| renew_thread                                                      |
+-------------------------------------------------------------------+
| UCORE_RENEW_universe/dble-v3/ushard-1/lock/ddl_lock/testdb.tablea |
+-------------------------------------------------------------------+
2 rows in set (0.00 sec)

注意:非集群模式下 或者 集群模式不为ucore时,查询的结果为空;

采用kill renew线程
mysql> kill @@cluster_renew_thread 'UCORE_RENEW_universe/dble-v3/ushard-1/lock/confChange.lock';
Query OK, 0 rows affected (0.00 sec)
kill cluster renew thread successfully!

可检索 kill日志:manual kill cluster renew thread
注意:在实际实现中,并不是在集群调度中心里直接把对应的分布式锁删除,而是通过中断dble内部renew线程,实现不再对分布式锁续约,达到分布式锁最终因超时而自行释放。

2.8.10 附录

legend

results matching ""

    No results matching ""