在分布式数据库架构下,当数据库节点异常时,数据库管理组件能够自动感知到异常并触发节点隔离或者自动切换,是数据库高可用容灾的基本能力。在节点服务器异常、网络异常或进程异常等场景下,各数据库产品本身已经具备了可靠的检测能力和恢复手段。但是在服务器磁盘异常或者IO缓慢等场景下,数据库本身对这类故障场景的检测能力如何,能否采取及时的故障恢复措施,本文将结合几种常见的国产分布式数据库进行分析。
1、OceanBase数据库
OceanBase数据库整体架构如图所示,一个集群由若干节点组成,这些节点组成不同的可用区Zone。可用区是逻辑概念,具有IDC和Region属性。OB数据库中表数据按照不同的规则划分为不同分片,每个分片称为分区Partition,分区又运行在不同的节点OBServer上。
在OceanBase数据库中分为系统租户和业务租户,其中系统租户中的Root Service承担了OceanBase数据库的大量管理工作,包括集群管理,租户管理,资源管理,负载均衡,每日合并调度,迁移复制等。
1.1 节点状态监控
作为集群的中控服务,Root Service 负责集群的节点管理,各节点通过心跳数据包(heartbeat)的方式,定期(每 2s)向 Root Service 汇报自己的进程状态,Root Service 通过监测节点的心跳数据包,来获取当前 observer 进程的工作状态。
- lease_time:当 Root Service 累计超过 lease_time 时间(默认10s)没有收到过某节点的任意心跳数据包时,Root Service 认为该 observer 进程短暂断线,Root Service 会标记该节点的心跳状态为 lease_expired。
- server_permanent_offline_time:当Root Service累计超过 server_permanent_offline_time时间(默认3600s)没有收到过某节点的任意心跳数据包时,Root Service认为该observer进程断线,Root Service会标记该节点的心跳状态为 permanent_offline。
Root Service 根据心跳数据包可以获得OBServer如下的工作状态:
- OBServer心跳数据包存在,心跳数据包中的 OBServer 磁盘状态正常。此种状态下,Root Service 认为 OBServer 处于正常工作状态。
- OBServer心跳数据包存在,心跳数据包中的OBServer磁盘状态异常。此种状态下,Root Service认为observer进程还在,但OBServer磁盘故障。此种状态下,Root Service会尝试将该OBServer上的全部leader副本切走。
- OBServer心跳数据包不存在,OBServer心跳数据包的丢失时间还比较短,OBServer心跳状态为lease_time,此种状态下,Root Service仅将OBServer的工作状态设置为inactive,不做其他处理。
- OBServer心跳数据包不存在,OBServer心跳数据包丢失时间超过server_permanent_offline_time,OBServer的心跳状态为permanent_offline,此种情况下,Root Service会对该OBServer上的数据副本进行处理,将该OBServer上包含的数据副本从Paxos成员组中删除,并在其他可用OBServer上补充数据,以保证数据副本Paxos成员组完整。
1.2 节点隔离
ODP(OBProxy)会感知OBServer的异常状态,例如stopped(ACTIVE 状态且stop_time字段大于0)状态、INACTIVE 状态等,从而避免将应用流量路由到异常节点上。如果某个节点网络不稳定、存在响应慢等异常情况,客户端连接该节点后业务侧也会出现不稳定。OceanBase数据库中提供了STOP SERVER命令用于隔离节点。隔离后,该节点不会对外提供服务,ODP 也不会把请求路由到该节点上,可以安全的进行诊断/替换/维修等动作。
Stop Server会检查所有剩余副本是否满足多数派、是否日志同步,当条件不满足的时候Stop Server操作会返回失败;条件满足的情况下等待故障节点上的Leader全部切走后系统会返回成功。Stop Server成功后保证能够做任何运维动作,包括Kill observer进程。节点隔离适用于单机房故障场景:
- 非通断型故障(例如网卡丢包导致的机器不稳定)强依赖节点隔离,否则会发生反复切主而影响应用。
- 通断型故障(例如机器直接宕机)不强依赖节点隔离,其会在oceanbase.DBA_OB_SERVERS视图标记节点状态为INACTIVE而隔离应用流量。
1.3 故障节点恢复
集群中发生故障的节点存在两种情况:
- 故障节点可以重新启动。这种情况下,不论故障机器之前处于那种心跳状态,重新启动后,OBServer节点与Root Service之间的心跳数据包恢复以后,节点可重新提供服务。
- 故障节点损坏,无法重新启动。这种情况下,在确认节点损坏无法重新启动后,需要数据库管理员执行集群管理操作,将该节点中删除。
2、GoldenDB数据库
DN节点上部署有MySQL和dbagent,其中dbagent就是对MySQL进行周期性检测的组件。Dbagent根据相关配置,对MySQL进行检测,相关配置在dbagent.ini中。DBagent会上报心跳信息到管理节点CM,由CM统一进行故障节点的切换处理。
2.1 周期性检查
Dbagent按照配置的周期,对于mysql进行周期性的检查,根据检查结果设置DB不同的状态,并周期性的将状态上报给CM。CM再根据不同的状态,进行不同的动作。监控周期配置参数:monito_interval,默认为5s。
2.2 长短连接
Dbagent和MySQL之间存在长连接,以及每次检测周期都会建立一个短连接,以用来探测MySQL是否OK。
- 长连接主要是用来执行探测的SQL,根据执行结果判断MySQL是否OK;
- 短连接主要用来进行MySQL连接,判断MySQL是否可以建链
建立连接超时时间由参数控制:connect_database_timeout=40
2.3 探测MySQL
首先判断mysqld进程是否存在,不存在则需要重新拉起;其次,需要对MySQL进行检测,以判断其是否正常。Dbagent建立心跳表dbagent.heartbeat,用于探测MySQL能否进行DML操作,探测语句由长连接执行。Dbagent根据是否处于特殊窗口期和长连接的执行结果以及短连接的连接结果进行综合判断MySQL的状态
长连接(更新心跳表)+短连接(仅作db连接),测试结果如下:
- 长连接正常+短连接正常,记为状态A
- 长连接正常+短连接异常,记为状态B
- 长连接异常+短连接正常,记为状态C
- 长连接异常+短连接异常,记为状态D
优先使用窗口策略(即检查时间点落在配置项monitor_loose_period时间段区间内),长短连接有一个异常(即为状态BCD中任何一种),则放大阈值,其值为两个配置项乘积(connect_test_try_max_times*monitor_loose_times),当失败次数累加值达到阈值,且配置项monitor_kill_db_after_detect_fail为1,则kill db;
非窗口期内则采用收缩策略,状态为B或C则放大阈值,其值使用两个配置项乘积(connect_test_try_max_times*monitor_enlarge_times);状态D则收缩阈值,其值为配置项connect_test_try_max_times的值,并重置B/C状态的失败累加计数器,当失败次数累加值达到阈值,且配置项monitor_kill_db_after_detect_fail为1,则kill db;跳转到状态A,清理BCD状态的历史失败累加值。
monitor_loose_period默认值在00:00:00~00:00:00、monitor_loose_times默认值为10、monitor_enlarge_times默认值为50.
2.4 备机复制关系
Dbagent在监控备机的时候,还会监控备机的复制关系。通过show slave status命令获取到当前复制关系和状态,并上报到CM节点,如果发现复制关系有异常,尝试重试启停复制关系并尝试自动修复。
2.5 DN节点主备故障
DN节点的主备故障恢复,由CM组件进行统一管理。CM节点通过dbagent上报的心跳信息,来判断主备是否异常。如果CM丢失了dbagent心跳,对应的DN状态无法通过dbagent获取,丢失心跳超过一定次数,则会认为该DN节点异常。如果是主节点,CM还会通过其它备机的复制关系获取当前主的状态来辅助判断MySQL是否真的异常,仅仅dbagent异常而MySQL正常,CM也不会认为主节点异常。
- 检测dbagent心跳的周期:参数check_lost_heartbeat_interval,默认值为10s
- 允许丢失dbagent心跳的次数:参数max_allow_loss_heartbeats_times,默认值为3
3、TiDB数据库
TiDB数据库中的PD(Placement Driver)是整个集群的元信息管理模块,负责存储每个TiKV节点实时的数据分布情况和集群的整体拓扑结构。PD还会根据TiKV节点实时上报的数据分布状态,下发数据调度命令给具体的TiKV节点。
TiKV集群会向PD汇报两类信息:TiKV的节点状态信息和Region信息。
- TiKV节点与PD之间的心跳包,包括节点的存活状态、是否有新的节点加入,以及状态信息,包括:磁盘容量、Region数量、数据读写速率、Snapshot数量、是否过载以及label标签信息
- Region状态信息,包括Leader的位置、Follower位置、掉线副本的个数、数据读写速率。
- Up:表示当前的 TiKV Store 处于提供服务的状态。
- Disconnect:当PD和TiKV Store的心跳信息丢失超过20秒后,该Store的状态会变为Disconnect状态,当时间超过max-store-down-time指定的时间后,该Store会变为Down状态。
- Down:表示该TiKV Store与集群失去连接的时间已经超过了max-store-down-time指定的时间,默认30分钟。超过该时间后,对应的Store会变为Down,并且开始在存活的Store上补足各个Region的副本。
- Offline:当对某个TiKV Store通过PD Control进行手动下线操作,该 Store 会变为 Offline 状态。该状态只是 Store 下线的中间状态,处于该状态的Store会将其上的所有Region搬离至其它满足搬迁条件的Up状态Store。当该Store 的leader_count和region_count均显示为 0 后,该Store会由Offline状态变为Tombstone状态。在Offline状态下,禁止关闭该 Store 服务以及其所在的物理服务器。下线过程中,如果集群里不存在满足搬迁条件的其它目标 Store(例如没有足够的 Store 能够继续满足集群的副本数量要求),该 Store 将一直处于 Offline 状态。
- Tombstone:表示该TiKV Store已处于完全下线状态,可以使用 remove-tombstone 接口安全地清理该状态的 TiKV。
另外,PD 还可以通过扩展的接口接受额外的信息,用来做更准确的决策。比如当某个 Store 的心跳包中断的时候,PD 并不能判断这个节点是临时失效还是永久失效,只能经过一段时间的等待(默认是 30 分钟),如果一直没有心跳包,就认为该 Store 已经下线,再决定需要将这个 Store 上面的 Region 都调度走。
TiDB v6.4.0 全面优化了 TiKV 节点的状态检测机制。即使在磁盘故障或 I/O 无响应等极端情况下,TiDB 依然可以快速上报节点状态,同时搭配主动唤醒机制,提前发起 Leader 选举,加速集群自愈。
4、GaussDB数据库
GaussDB(for openGauss)通过集群管理组件CM server和CM agent来进行集群的管理。CM组件提供了四种服务CM Agent、CM Server、OM Monitor、cm_ctl,与各类实例服务组件(CN, DN, GTM 等)一起构成了整个数据库集群系统。
4.1 CM运行流程
4.1.1 cm_server功能
cm_server是集群 管理的大 脑,整个管理系统的仲裁者,系统中只有一个,主备架构,系统中常驻的服务进程,多线程架构。主要收集cm_agent返回的各个实例(GTM主备、CN、DN主备从)状态信息,从而通过分析给出谁是主谁是备,什么时候需要failover切换,什么时候需要重建DN。cm_server的另一个作用就是接收客户端cm_ctl发送的命令,使得cm_ctl可以管理整个集群。
4.1.2 cm_agent功能
cm_agent负责收集本地内核端各个组件的状态信息,并上报给CMserver。CMagent本地并不存储任何信息,是一个无状态的组件,进程故障后,可以由OM monitor立刻拉起。
4.1.3 om_monitor
om_monitor进程负责拉起cm_agent以及杀掉非自己启动的cm_agent。om_monitor为了保活,每分钟都会被系统定时任务crontab拉起,后拉起的进程在发现已经有om_monitor进程时,会自己退出。
4.2 CM告警机制
cm_agent会实时检测cn、dn、gtm、cm_server状态,固定间隔1秒上报实例状态给cm_server。心跳是在cm_server上累计的。没有上报消息,心跳就会+1,超时就会仲裁。只有cm_agent成功连接并上报节点信息,才会刷新心跳状态。
- 当cm_agent检测到dn或gtm实例状态异常并上报给cm_server之后,会做一些检查以及启动实例,会根据实例心跳超时时间(instance_heartbeat_timeout,默认值6s)进行判定,当超过实例心跳时间之后实例无法恢复,cm_server会给cm_agent下发仲裁命令,将相应实例的状态置为 Unknown。
- 当cm_agent检测到cn实例状态异常之后,会做一些检查以及启动实例,会根据cm_server在CN故障后仲裁自动剔除的触发时间(coordinator_heartbeat_timeout,默认值25s),当超过该时间实例无法恢复,自动剔除cn。
- 当cm_server未收到cm_agent的上报,会根据实例心跳超时时间(instance_heartbeat_timeout)进行判定,当超过实例心跳时间之后依旧无法恢复cm_server与cm_agent的连接,cm_server会给其他cm_agent下发主备切换,将相应实例的状态置为 Unknown。
4.3 异常检测
数据库实例可能处于故障、僵死、亚健康等状态。这些状态的出现概率逐级降低,但检测难度逐级增高。CM 组件包含异常检测流程,可用于识别和处理网络不稳定、磁盘 IO 挂死、进程/线程僵死、进程频繁退出、实例状态时好时坏等场景,提高集群的稳定性和可用性。以DN节点为例,包括长连接和短连接检测:
- CM Agent和实例之间建立长连接,当长连接断开直到超出心跳时间,cms发送failover命令到备节点,触发主备切换;
- 短连接检测中CM Agent按照固定时间间隔与DN实例新建链接。如果与某个主DN链接失败或通过新链接无法执行SQL语句,则认为该DN发生1次Hang异常。如果多次出现异常(instance_keep_heartbeat_timeout,默认值40s),则会触发Hang检测机制,将该DN实例杀死并执行主备切换。目前常见的异常检测项有:短连接建立、短连接执行SQL语句异常、IO挂死和内存异常等。
4.4 故障恢复
常见故障类型包括:磁盘故障、磁盘损坏、慢盘、RAID卡损坏、网络故障、断网、闪断、时延、丢包、节点下电或重启、操作系统故障、磁盘空间不足、文件句柄耗尽、进程异常退出、进程僵死等。针对不同的实例故障,CM恢复手段也不同:
- CN节点故障:将故障CN节点剔除
- DN节点故障:主DN故障时,仲裁备DN升主继续提供服务;备DN故障时,主DN将日志和数据同步至从备,业务不受影响
- 主GTM故障:备GTM升主后继续提供服务
- 主CM Server故障:备CM Server接受多数派CM Agent链接,升主后继续提供服务
以主DN节点故障为例,主备切换仲裁流程如下:
- CM Agent 1探测DN主实例并发现故障
- CM Agent 1持续上报实例故障信息至CM Server
- CM Server执行仲裁流程,选择DN备机升主
- CM Server下发升主命令至CM Agent 2
- CM Agent 2对实例执行升主操作
5、TDSQL数据库
5.1 TDSQL架构
在每一个MySQL的实例上都有一个Agent,可以将它看做一个旁路的模块,是集群中的各个节点之间进行信息交互的桥梁,主要功能如下:
- 监控MySQL的实例是否能够正常的运行,及时上报实例异常的信息,便于Scheduler模块判断是否需要进行容灾切换,同时会监测是否有容灾切换的任务
- 监测MySQL实例的资源利用率、各个表的请求量和数据量,并上报到Zookeeper。Scheduler模块根据资源的使用情况判断是否需要进行扩容或者缩容;同时会监测是否有扩容或缩容的任务下发
- 监测主从复制和数据同步的情况,定期上报主备复制的延时和延迟的事物数,如果发生了主备切换会自动向新主机重建主备
- 迁移、配置文件修改、表一致性校验、binlog\镜像备份等任务的检测
Agent会尝试通过Socket连接DB、写DB和读取DB,如果这三个操作中有一个不成功即上报存活状态异常,Scheduler会根据异常信息进行容灾切换。Agent在Zookeeper中存储的节点类型是临时节点,当Agent出现异常临时节点会消失,此时Scheduler会检测到对应的异常并处理。Scheduler控制整个切换的流程,Scheduler会分析Agent上报上来的心跳信息,决策是否进行主备切换,控制切换流程。
5.2 主从切换流程
TDSQL 中通过mysqlagent 组件对 mysqld 存活状态进行探测,探测是每秒执行一次,探测使用socket连接,忽略强同步的复制逻辑;然后每3秒上报到 zookeeper 上的心跳节点。探测方法有如下三种:
- 读心跳表,检查是否可读(长连接)。
- 写心跳表,检查是否可写(长连接)。
- 尝试建立到 mysqld 的新 TCP 连接,检查 mysqld 是否能接受新连接。
scheduler从zookeeper中获取主DB的hb心跳内容(conn_err、read_err、write_err)来判断DB存活状态是否正常,检查字段(conn_err、read_err、write_err)中只要有一个是非0值就认为故障(心跳异常);此时schedule会记录故障开始时间到setrun中,并更新状态为不存活(alive为-1)。
- 心跳节点正常上报,zookeeper中hb心跳内容明确展示mysqld异常。该情况处理比较简单,直接根据心跳内容来判断即可。
- 心跳节点无法上报,zookeeper中hb心跳内容的临时节点丢失,即hb@xxx的信息丢失。
针对心跳节点无法上报的情况,schedule会读取zookeeper Watch机制触发的delete事件主动写setrun信息,包括delete事件触发时间戳和存活状态。
- 当set主节点异常(心跳丢失和心跳内容错误),scheduler开始计时(schedule本机时间减hb心跳时间);当set主节点心跳在maxlosthbtime(默认20s)时间内没有恢复时,则scheduler开始触发主从切换。
- 切换前,先检测当前实例是否处于免切状态(整个集群手动免切、set手动免切、set自动免切)。如果处于免切状态,并且在有效时间窗口内,则不触发主从切换,同时将不切换原因入库。
- 判断当前set是否为扩容、回档、dcn数据同步等状态(setrun中的kpstatus值),如果是则不触发主从切换。
- 统计同IDC与跨IDC的存活强同步从机个数;这里存活的定义是:alive为0,同时election为true。同时检测watch节点中是否存在standby_watch,如果存在,并且时间小于30s,则可以作为选举从机节点。
6、总结
从上文可以看出,各数据库产品针对实例服务器异常、网络异常等场景已经具备了可靠的检测能力和恢复手段。针对服务器磁盘异常或者IO缓慢等场景,如下表现:
- OceanBase数据库通过Root Service接收节点的心跳信息,在心跳信息中包含有磁盘的状态信息。当磁盘故障时,Root Service会尝试将该OBServer上的全部leader副本切走。也就是OB数据库对磁盘异常这一故障场景做了处理。
- GoldenDB数据库通过长连接和短连接的方式,将心跳信息上报给CM节点。针对磁盘异常或IO慢的场景,心跳中没有明确的信息给到CM节点进行判断,因此在磁盘异常导致的IO夯住等场景下,故障的恢复时间变长;
- TiDB数据库将心跳信息上报给PD,出现异常时由PD触发调度策略。TiDB在6.4版本后,增加了在磁盘故障或 I/O 无响应等极端情况下,TiDB快速上报节点状态,同时搭配主动唤醒机制,减少故障恢复时间。
- GaussDB数据库通过cm_agent将心跳信息上报到cm_server,故障检测的场景包括磁盘故障和慢盘等场景
- TDSQL数据库也是通过agent对mysql进程的长短连接的方式,上报心跳信息。针对磁盘故障IO夯住这一类场景,故障恢复时间也将变长。
参考资料:
- https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000640611
- GoldenDB数据库高可用切换机制
- https://docs.pingcap.com/zh/tidb/stable/tidb-scheduling
- https://bbs.huaweicloud.com/blogs/224005
- https://cloud.tencent.com/privatecloud/document
- https://blog.51cto.com/u_15721050/6038871