专家观点
当前,国内大量的关键行业的核心系统正在实现国产化替代,而与此同时,这些行业的数字化转型也正在进入深水区。在信息系统的升级换代过程中,夯实 IT 基础设施是极其关键的。从服务器、操作系统、中间件、数据库等基础软硬件选型到系统架构、应用架构的重新设计,再到数据迁移、系统迁移、系统优化、运维体系重构的一系列工作都是十分具有挑战性的。大多数工作中,都会遇到无法完全参考前人的探索和创新。
一些勇敢的先行者已经在这条荆棘丛生的道路上硬生生的创出了一条血路,而更多的人还在未知与迷茫中摸索。“银⾏核⼼背后的落地⼯程体系”系列技术文章来自于金融行业用户真实故事,从中可以窥见的不仅是国产化替代路上的艰辛历程,更多的是对于后来者极其宝贵的实践经验。
在本系列文章中你不仅可以看到 TiDB 与 Oracle 的异构迁移、基于 TiCDC 的逻辑容灾与数据共享、构建双活数据中心的真实案例,还可以了解到混沌测试、应急预案等方面,以及利用数据库可观测性构建智能化、自动化运维体系的方案与技巧。“积小流,成江海”,希望这些案例能够对您有所裨益。
特邀专家:
徐戟(白鳝)
资深数据库技术专家,订阅号“白鳝的洞穴”作者
【系列推荐】
TiDB DR-Auto-Sync 是 TiDB 分布式数据库同城双中心高可用方案,在银行核心业务场景中得到了充分的实践验证。与常见的 TiDB 三中心部署方案类似,DR-Auto-Sync 同样能满足机房级 RPO=0 的高可用能力,两中心间的数据复制也是通过集群自身的 Raft 机制完成。两中心可同时对外进行读写服务,任一中心发生故障不影响数据一致性。

sync:同步复制模式,此时副中心至少有一个副本与主中心保持同步,Raft 算法保证每条日志按 Label 标签同步复制到副中心。
async:异步复制模式,此时不保证副中心与主中心完全同步,Raft 算法使用经典的 majority 方式复制日志。
sync-recover:恢复同步模式,此时不保证副中心与主中心完全同步,各 region raft 逐步切换成同步复制模式(主/副中心均有最新 raft log 落地),切换成功后汇报给 PD。
Voter:TiKV 实例相关 region peer 参与 raft 选主投票,并可以被推举为 Leader。
Follower:TiKV 实例相关 region peer 参与 raft 选主投票,但不可以被推举为 Leader。
Learner:TiKV 实例相关 region peer 只从 Leader 实时同步 raft log,不参与 raft 选主投票,也不可以被推举为 Leader。
集群采用推荐的 6 副本模式,其中主中心 (DC1) 3 副本 TiKV 均为 Voter,副中心 (DC2) 2 副本 TiKV 为 Follower,1 副本 TiKV 为 Learner。
PD 采用 5 实例部署,PD Leader 位于主中心 (DC1),并且实例优先级高于副中心 (DC2) 实例。
TiDB 计算双活,应用通过负载均衡访问同中心的 TiDB 实例。

调整 TiKV 心跳上报频率,pd-store-heartbeat-tick-interval,与 Dr-Auto-Sync 相关的 wait-store-timeout 同频可能导致 sync/async 切换无故自动切换问题。
调整 snapshot 相关参数,规避 sync_recover 阶段 snapshot 传输导致的 QPS 下降问题。
为 TiKV 实例配置 dc (数据中心) 以及 logic (逻辑单元,部署时与机柜对应) 标签 (注意:相同 logic 标签的 TiKV 实例承载一份完整的数据副本)。
server_configs:
tidb:
proxy-protocol.networks: 192.168.1.70 #IP透传能力,硬件负载配置需要对应调整
tikv:
raftstore.pd-store-heartbeat-tick-interval: 2s
raftstore.snap-generator-pool-size: 1 #调整这个和下面的参数降低 sync_recovery 时候的 snapshot 传输速度,以避免该阶段 QPS 下跌的太严重
server.snap-max-write-bytes-per-sec: 30MB
server.concurrent-send-snap-limit: 4
server.concurrent-recv-snap-limit: 4
pd:
replication.max-replicas: 5 ##副本数量
replication.location-labels: ["dc", "logic","host"]
replication.isolation-level: "logic"
tikv_servers:
- host: 192.168.1.1
config:
server.labels:
dc: dc1
host: "192_168_1_1"
logic: logic1
arch: arm64
os: linux
- host: 192.168.1.2
config:
server.labels:
dc: dc1
host: "192_168_1_2"
logic: logic2
- host: 192.168.1.3
config:
server.labels:
dc: dc1
host: "192_168_1_3"
logic: logic3
- host: 192.168.2.1
config:
server.labels:
dc: dc2
host: "192_168_2_1"
logic: logic4
- host: 192.168.2.2
config:
server.labels:
dc: dc2
host: "192_168_2_2"
logic: logic5
- host: 192.168.2.3
config:
server.labels:
dc: dc2
host: "192_168_2_3"
logic: logic6
pd_servers:
- host: 192.168.1.101
- host: 192.168.1.102
- host: 192.168.1.103
- host: 192.168.2.101
- host: 192.168.2.102
monitoring_servers:
- host: 192.168.1.101
grafana_servers:
- host: 192.168.1.101
alertmanager_servers:
- host: 192.168.1.101
- host: 192.168.4.101复制
#修改placement rule配置
tiup ctl:v6.5.6 pd -u 192.168.1.1:2379 config placement-rules rule-bundle save --in="/home/tidb/dr_auto_sync_dc1_rule.json"
#查看当前placement rule
tiup ctl:v6.5.6 pd -u 192.168.1.1:2379 config placement-rules show
#/home/tidb/dr_auto_sync_dc2_rule.json
[
{"rules": [
{"group_id": "pd","id": "dc1-logic1","role": "voter","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic1"]}]
},
{"group_id": "pd","id": "dc1-logic2","role": "voter","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic2"]}]
},
{"group_id": "pd","id": "dc1-logic3","role": "voter","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic3"]}]
},
{"group_id": "pd","id": "dc2-logic4","role": "follower","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic4"]}]
},
{"group_id": "pd","id": "dc2-logic5","role": "follower","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic5"]}]
},
{"group_id": "pd","id": "dc2-logic6","role": "learner","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic6"]}]
}
]}
]复制
tiup ctl:v6.5.6 pd -u 192.168.1.1:2379 -i
#修改dr-auto-sync 模式
config set replication-mode dr-auto-sync
#配置dr-auto-sync 的机房标签
config set replication-mode dr-auto-sync label-key dc
#配置主中心
config set replication-mode dr-auto-sync primary dc1
#配置副中心
config set replication-mode dr-auto-sync dr dc2
#配置主中心副本数量
config set replication-mode dr-auto-sync primary-replicas 3
#配置副中心非learner副本数量
config set replication-mode dr-auto-sync dr-replicas 2
#当出现网络隔离或者故障时,降级为异步模式的等待时间
config set replication-mode dr-auto-sync wait-store-timeout "15s"复制
计划内切换:有计划的主中心、副中心角色转换,常用于日常容灾演练、副中心业务验证、计划内机房硬件维护等场景,具备操作简单、业务无感知等优点。
计划外切换:应对主中心机房级故障导致的集群不可用,通过人工干预快速恢复集群并对外提供服务。操作相对复杂,在大部分场景下提供 RPO=0、RTO<1 min(从中心故障) 或 RTO<10 min(主中心故障)的容灾保障。


#获取pd实例信息
tiup cluster display tidb-test -R pd
#选择DC1机房非pd leader 实例进行缩容操作,规避pd leader 切换带来的业务影响
tiup cluster scale-in tidb-test -N 192.168.1.2:2379
#扩容DC2机房pd实例
pd_servers:
- host: 192.168.2.5
ssh_port: 22
name: pd-192.168.2.5-2379
client_port: 2379
peer_port: 2380
arch: arm64
os: linux
tiup cluster scale-out tidb-test pd-scale.yaml -uroot -p复制
通过修改 Placement Rules 配置,完成主/副中心 TiKV 角色转换 (DC1:2 Follower + 1 Learner,DC2:3 Voter)。PD Leader 根据新的 Placement Rules 配置,重新调度 Region Leader。
pd-ctl 调整 DR-Auto-Sync primary/dr dc 配置,完成数据中心角色配置对换。
循环统计各 TiKV Region Leader 数量,确保 DC1 Region Leader 数为 0。
######placement-rule配置修改#######
tiup ctl:v6.5.6 pd -u 192.168.1.1:2379 config placement-rules rule-bundle save --in="/home/tidb/dr_auto_sync_dc2_rule.json"
#/home/tidb/dr_auto_sync_dc2_rule.json
[
{"rules": [
{"group_id": "pd","id": "dc1-logic1","role": "follower","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic1"]}]
},
{"group_id": "pd","id": "dc1-logic2","role": "follower","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic2"]}]
},
{"group_id": "pd","id": "dc1-logic3","role": "learner","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic3"]}]
},
{"group_id": "pd","id": "dc2-logic4","role": "voter","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic4"]}]
},
{"group_id": "pd","id": "dc2-logic5","role": "voter","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic5"]}]
},
{"group_id": "pd","id": "dc2-logic6","role": "voter","count": 1,
"location_labels": ["dc", "logic", "host"],
"label_constraints": [{"key": "logic", "op": "in", "values": ["logic6"]}]
}
]}
]
#####集群主副中心角色转换####
#调整PRIMARY DC
tiup ctl:v6.5.6 pd -u 192.168.1.1:2379 config set replication-mode dr-auto-sync primary dc2
#调整DR DC
tiup ctl:v6.5.6 pd -u 192.168.1.1:2379 config set replication-mode dr-auto-sync dr dc1
#######循环统计region leader 方法
function drautosynccheck()
{
local cluster_name="$1"
local cluster_version="$2"
local pd_leader_addr="$3"
local new_primary_dc="$4"
local new_dr_dc="$5"
echo "开始监查副region leader信息"
second_dc_leader_list=0
check_status=0
for ((i=1; i<= 100 ; ++i)) do
tiup ctl:$cluster_version pd -u $pd_leader_addr store --jq='.stores[]|{dc:.store|.labels[],address:.store|.address,leader_count: .status|.leader_count}'|grep $new_dr_dc
second_dc_leader_list=`tiup ctl:$cluster_version pd -u $pd_leader_addr store --jq='.stores[]|{dc:.store|.labels[],address:.store|.address,leader_count: .status|.leader_count}'|grep $new_dr_dc|jq 'select(.leader_count!=0)'|wc -l`
if [ "$second_dc_leader_list" -gt "0" ];then
echo "副机房还有region leader"
sleep 15
else
echo -e "副机房无region leader"
check_status=1
break
fi
done
if [ "$check_status" -ne "1" ]; then
exit 1
fi;
}
drautosynccheck()复制
将副中心 (DC2) 3 个 PD 实例优先级提升为 100,主中心 (DC1) 2 个 PD 实例权重降低为 50。确保 PD Leader 与 Region Leader 一样位于副中心,从而减少数据读写访问的网络延迟,提升业务性能。
选择副中心 PD 实例,发起 PD Leader 迁移。(注意:PD Leader 切换期间少量业务查询因无法获取最新 ts 报错,持续大约 1s)
#设置pd实例优先级,确保pd leader始终处于主中心
tiup ctl:v6.5.6 pd -u 192.168.1.1:2379 -i
#DC2 pd实例
member leader_priority ${pd_name} 100
#DC1 pd实例
member leader_priority ${pd_name} 50
#触发pd leader切换
tiup ctl:v6.5.6 pd -u 192.168.1.1:2379 -i
#选择DC2 pd 实例
member leader transfer ${pd_name复制

TiKV 角色转换:TiKV 角色转换期间,通过 Region Leader 数量检测脚本实时统计 TiKV Region Leader 数量,确保 DC1 主机房相关 Leader 迁移完成。

PD Leader 切换:如下图所示,PD Leader transfer 操作大约只需要 500 ms 即可完成 Leader 的角色转移,业务影响大约只有 1 s。

DR-Auto-Sync 的计划外切换主要用于在主数据中心发生故障后迅速恢复,以确保集群能够继续对外提供服务。以下是不同复制模式下,计划外切换后集群的数据形态,目前支持 sync/async 模式下的计划外修复工作。
切换步骤主要涉及确认集群复制模式,检查相关工具,重建 meta.yaml 文件,计划外抢修(重构 PD、unsafe recovery)等工作。

#通过api实时获取集群同步状态
curl http://{pd_leader_ip}:{pd_port}/pd/api/v1/replication_mode/status复制
leader_count=`tiup ctl:$cluster_version pd -u $pd_leader_addr store --jq='.stores[]|{dc:.store|.labels[],address:.store|.address,leader_count: .status|.leader_count}'|grep $dr_dc|jq 'select(.leader_count!=0)'|wc -l`
if [ "$leader_count" -gt "0" ];then
echo "触发告警"
else
echo "状态正常"
fi复制
#通过prometheus获取最新的pd leader 信息
curl http://{prometheus_url}/api/v1/query?query=pd_tso_role(dc="global",instance="{pd_id}:{pd_port}")复制
#edit-config 修改
raftstore.pd-store-heartbeat-tick-interval: 2s
#pd-ctl修改
tiup ctl:v6.5.4 pd -u 192.168.1.1:2379 config set replication-mode dr-auto-sync wait-store-timeout 15s复制
#调整这下面的参数降低 sync_recovery 时候的 snapshot 传输速度,以避免该阶段 QPS 下跌的太严重
raftstore.snap-generator-pool-size: 1
server.snap-max-write-bytes-per-sec: 30MB
server.concurrent-send-snap-limit: 4
server.concurrent-recv-snap-limit: 4
raftstore.store-io-pool-size: 2
raftstore.apply-pool-size: 4复制
/ 阅读推荐 /
银行核心背后的落地工程体系丨Oracle - TiDB 数据迁移详解

💡 点击文末【阅读原文】,立即下载试用 TiDB!
