ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

MySQL切人大金仓,半夜主备切换搞出“双主脑裂”差点进去踩缝纫机!这份金仓V9高可用集群与防脑裂避坑指南,救了整个信创项目

MySQL切人大金仓,半夜主备切换搞出“双主脑裂”差点进去踩缝纫机!这份金仓V9高可用集群与防脑裂避坑指南,救了整个信创项目 目录降维剖析MySQL HA vs 金仓 HA 的底层逻辑差异基石金仓 V9 流复制Streaming Replication深度配置大脑etcd 集群与金仓 kbha 组件实战防脑裂核心工程实践C# 高可用混沌演练引擎Chaos Engineering性能调优与避坑指南让切换真正做到 RPO0总结与避坑清单一、降维剖析MySQL HA vs 金仓 HA 的底层逻辑差异 魔性比喻MySQL 的主从复制 就像是 “抄作业”主库把做过的题Binlog SQL/Row 事件发给备库备库自己重新做一遍。如果备库做得慢就会延迟。金仓PG内核的流复制 就像是 “复印试卷”主库直接把写好的物理数据页WAL 日志物理修改记录通过 TCP 流式传给备库备库直接“贴”到自己的数据文件上。速度极快且保证物理级别的一致。1.1 核心架构与防脑裂机制对比矩阵维度 MySQL 高可用生态 人大金仓 (KingbaseES V9) 高可用生态 核心差异与踩坑点复制机制 Binlog (逻辑/行级) WAL (Write-Ahead Log, 物理级) 金仓的流复制是物理拷贝备库与主库数据文件完全一致连 OID 都一样。主流 HA 方案 MHA, Orchestrator, InnoDB Cluster, Keepalived kbha (基于 etcd), sys_ha, Patroni, 读写分离集群 核心 金仓绝对不要用 Keepalived必须用基于分布式一致性etcd的 HA 组件。防脑裂 (Fencing) STONITH (Shoot The Other Node In The Head)关机或断网 etcd 分布式租约 (Lease) 触发器隔离 etcd 租约过期老主库会自动降级为备库或自杀从根本上杜绝双主。VIP 漂移 Keepalived VRRP 协议 kbha 调用自定义 VIP 脚本 (通过 etcd 触发) 金仓的 VIP 漂移是由 HA 组件在确认主备状态后主动调用的更安全。数据一致性(RPO) 半同步复制 (Semi-sync) 同步流复制 (Synchronous Replication) 金仓的 synchronous_commit remote_apply 可以保证 RPO0且备库立即可读。二、基石金仓 V9 流复制Streaming Replication深度配置高可用的前提是数据能实时、准确地同步到备库。金仓的流复制配置比 MySQL 稍微复杂一点但逻辑更严密。2.1 主库配置Masterkingbase.conf —— 主库流复制核心配置⚠️ 修改后需要重启数据库部分参数可 reloadWAL 级别必须 replica 支持物理流复制和归档logical 额外支持逻辑订阅CDC。生产环境高可用通常用 replica 即可性能最好。wal_level replica最大 WAL 发送进程数主库发给备库的进程 建议设置为 备库数量 2留余量给备份工具如 sys_basebackupmax_wal_senders 6最大复制槽Replication Slots数量 核心神器复制槽可以防止主库在备库还没同步完时就把 WAL 日志删除了。这是保证 RPO0 的底层基石max_replication_slots 6WAL 保留策略wal_keep_size 2GB # (PG13/KBV9) 至少保留 2GB 的 WAL防止网络抖动导致备库断流如果是老版本用 wal_keep_segments 128同步提交级别决定 RPO 的关键 remote_apply主库提交事务时必须等待至少一个同步备库接收并应用回放 了该 WAL。这保证了主库宕机时备库的数据和主库绝对一致RPO0且备库立即可读无延迟。性能代价事务延迟会增加网络 RTT 备库回放时间。synchronous_commit remote_apply同步备库名称与 standby 的 application_name 对应 ‘’ 表示任意一个同步备库确认即可。也可以指定具体名字如 ‘kb_node2’。synchronous_standby_names ANY 1 ()’sys_hba.conf —— 配置复制用户的网络访问权限允许备库 IP (192.168.1.200) 使用 replica 用户进行流复制连接 必须放在文件靠前的位置优先匹配host replication replica_user 192.168.1.200/32 scram-sha-256– 在主库创建专用的复制用户CREATE ROLE replica_user WITH REPLICATION LOGIN PASSWORD ‘YourStrongPassword123!’;– 创建物理复制槽强烈建议– 复制槽会“钉住”WAL 日志即使备库宕机主库也不会清理备库还没拉取的 WAL。– 防止备库重启后因为缺少 WAL 而不得不全量重建SELECT sys_create_physical_replication_slot(‘slot_node2’);2.2 备库搭建Standby—— 使用 sys_basebackup!/bin/bash备库一键初始化脚本 核心工具sys_basebackup (类似于 PG 的 pg_basebackup)它会通过流复制协议在线把主库的数据目录完整“克隆”到备库。export KB_HOME/opt/Kingbase/ES/V9export PATHKB_HOME/server/bin:PATH备库数据目录必须为空或不存在DATA_DIR“/data/kingbase/data”rm -rf DATA_DIR执行基础备份克隆数据 -Fp: 纯文本格式 (Plain) -Xs: 使用流式传输 WAL (Stream)保证备份期间产生的新数据也能带过来 -P: 显示进度 -R: 自动创建 standby.signal 文件并配置 kingbase.auto.conf (极其方便) -S: 指定使用主库上创建的复制槽 ‘slot_node2’sys_basebackup -h 192.168.1.100 -p 54321 -U replica_user-D DATA_DIR-Fp -Xs -P -R-S slot_node2修改备库的 kingbase.auto.conf (sys_basebackup -R 会自动生成这里做微调)cat DATA_DIR/kingbase.auto.conf EOF备库角色primary_conninfo ‘host192.168.1.100 port54321 userreplica_user passwordYourStrongPassword123! application_namekb_node2’primary_slot_name ‘slot_node2’ 热备模式允许备库提供只读查询服务读写分离必备hot_standby onEOF启动备库sys_ctl start -D DATA_DIR验证流复制状态ksql -U system -d prod_db -h 192.168.1.100 -c “SELECT client_addr, state, sync_state, sent_lsn, write_lsn, flush_lsn, replay_lsn FROM sys_stat_replication;” 如果看到 state‘streaming’, sync_state‘sync’ (或 ‘quorum’)说明同步流复制搭建成功三、大脑etcd 集群与金仓 kbha 组件实战防脑裂核心这是防脑裂的灵魂Keepalived 是基于 VRRP 广播的网络分区时两边都以为对方死了就会选出两个 Master脑裂。etcd 是基于 Raft 协议的分布式 KV 存储它要求“多数派Quorum”同意才能写入。网络分区时少数派节点绝对无法获取 etcd 的 Leader 租约从而从根本上杜绝脑裂。金仓 V9 提供的 kbha或企业版的 sys_ha就是深度集成了 etcd 的高可用守护进程。3.1 etcd 集群部署3节点防单点故障在 3 台服务器 (192.168.1.10, .20, .30) 上部署 etcd 集群这里只展示 node1 的启动命令其他节点类似etcd --name etcd1–data-dir /data/etcd–listen-client-urls http://192.168.1.10:2379–advertise-client-urls http://192.168.1.10:2379–listen-peer-urls http://192.168.1.10:2380–initial-advertise-peer-urls http://192.168.1.10:2380–initial-cluster etcd1http://192.168.1.10:2380,etcd2http://192.168.1.20:2380,etcd3http://192.168.1.30:2380–initial-cluster-token kbha-cluster–initial-cluster-state new3.2 kbha 配置文件与 VIP 漂移脚本kbha.yml —— 金仓 HA 组件配置文件scope: kb_prod_cluster # 集群名称etcd 中的 key 前缀namespace: /service/ # etcd 命名空间name: kb_node1 # 当前节点名称必须与 kingbase.conf 里的 application_name 一致restapi:listen: 192.168.1.100:8008connect_address: 192.168.1.100:8008etcd:hosts: 192.168.1.10:2379,192.168.1.20:2379,192.168.1.30:2379 核心TTL (Time To Live) 租约时间。如果节点在 30 秒内没有向 etcd 续约etcd 就会认为它死了触发 Failover。ttl: 30bootstrap:dcs:ttl: 30loop_wait: 10 # 每 10 秒检查一次集群状态retry_timeout: 10maximum_lag_on_failover: 1048576 # 1MB备库延迟超过 1MB 不允许被提升为主库synchronous_mode: true # 开启同步模式保证 RPO0postgresql:use_pg_rewind: true # 开启 pg_rewind老主库恢复后可以通过增量回滚快速变回备库无需全量重建use_slots: truepostgresql:listen: 192.168.1.100:54321connect_address: 192.168.1.100:54321data_dir: /data/kingbase/databin_dir: /opt/Kingbase/ES/V9/server/binauthentication:replication:username: replica_userpassword: YourStrongPassword123!superuser:username: systempassword: YourSuperPassword! 核心回调脚本Callbacks当节点角色发生变化时kbha 会调用这些脚本。我们在这里实现 VIP 漂移和告警callbacks:on_start: /opt/scripts/vip_manage.shon_stop: /opt/scripts/vip_manage.shon_role_change: /opt/scripts/vip_manage.sh!/bin/bash/opt/scripts/vip_manage.sh —— VIP 漂移与 Fencing 脚本 参数1 action (on_start, on_stop, on_role_change)2 role (master, replica)3 cluster_nameVIP“192.168.1.88”NET_IFACE“eth0”ACTION1ROLE2echo “(date) - Action: ACTION, Role: ROLE” /var/log/kbha_vip.logif [ “ROLE” “master” ]; then# 当前节点被提升为主库绑定 VIPip addr add VIP/24 dev NET_IFACE label {NET_IFACE}:vip 2/dev/null# 发送免费 ARP (Gratuitous ARP)通知交换机更新 MAC 地址表arping -q -A -c 3 -I NET_IFACE VIP# 发送飞书/钉钉告警 curl -X POST https://oapi.dingtalk.com/robot/send?access_tokenxxx \ -H Content-Type: application/json \ -d {msgtype: text, text: {content: [金仓HA] (hostname) 已提升为主库VIP: VIP}}elif [ “ROLE” “replica” ] || [ “ACTION” “on_stop” ]; then# 当前节点降级为备库或停止解绑 VIPip addr del VIP/24 dev NET_IFACE label {NET_IFACE}:vip 2/dev/null# Fencing (隔离) 动作如果是老主库被踢下台为了防止它“诈尸”继续写数据 # 最安全的做法是直接触发关机或断开网络STONITH。 # 这里为了演示我们强制将数据库设为只读模式。 if [ ACTION on_role_change ]; then su - kingbase -c ksql -U system -d prod_db -c ALTER SYSTEM SET default_transaction_read_only on; SELECT sys_reload_conf(); fifi四、工程实践C# 高可用混沌演练引擎Chaos Engineering 金句“没有经过‘拔网线’测试的高可用架构就像没经过碰撞测试的汽车——你永远不知道它会在什么时候要了你的命。”很多团队搭完 HA 就不管了。真正的工程实践是引入混沌工程Chaos Engineering用代码自动、随机地制造故障验证 HA 的真实反应。下面我用 C# .NET 8 写一个后台服务它会定时执行“主库断网”、“主库进程强杀”等演练并通过 etcd API 和数据库连接池验证切换时间RTO和数据一致性RPO。4.1 混沌演练引擎C# 核心代码// // 人大金仓高可用混沌演练引擎 (Chaos Engineering)//// 设计思想// 1. 使用 SSH.NET 远程执行故障注入命令如 kill -9, iptables 断网。// 2. 使用 Npgsql 持续探测 VIP 的连通性和读写状态。// 3. 使用 etcdnet 监控集群 Leader 的切换过程。// 4. 精确计算 RTO恢复时间目标和验证 RPO恢复点目标。//// ⚠️ 警告此代码仅用于测试/预发环境严禁在未经审批的生产环境运行// namespace Xinchuang.Kingbase.ChaosEngine;using System.Diagnostics;using System.Net.Sockets;using System.Text;using Npgsql;using Renci.SshNet;using Microsoft.Extensions.Logging;public class KingbaseChaosOrchestrator{private readonly ILogger _logger;private readonly ChaosConfig _config;public KingbaseChaosOrchestrator( ILoggerKingbaseChaosOrchestrator logger, ChaosConfig config) { _logger logger; _config config; } /// summary /// 执行完整的“主库宕机”混沌演练 /// /summary public async TaskChaosReport ExecuteMasterCrashDrillAsync(CancellationToken ct) { var report new ChaosReport { DrillType Master_Crash, StartTime DateTime.Now }; var sw Stopwatch.StartNew(); try { // 1. 确认当前谁是主库通过 VIP 连接查询 _logger.LogInformation( [Step 1] 探测当前主库 ); var currentMaster await GetCurrentMasterAsync(ct); report.OriginalMaster currentMaster; // 2. 在主库写入一条“哨兵数据”用于验证 RPO _logger.LogInformation( [Step 2] 写入哨兵数据 ); var sentinelId await WriteSentinelDataAsync(ct); report.SentinelId sentinelId; // 3. 启动后台持续探测任务计算 RTO _logger.LogInformation( [Step 3] 启动持续探测 ); var probeTask StartContinuousProbingAsync(report, ct); // 4. 注入故障强杀主库的 kingbase 进程 (kill -9) _logger.LogInformation( [Step 4] 注入故障强杀主库进程 ); await InjectFaultAsync(currentMaster, kill -9 $(head -1 /data/kingbase/data/postmaster.pid), ct); report.FaultInjectedAt DateTime.Now; // 5. 等待 VIP 漂移和备库提升监听探测任务的结果 _logger.LogInformation( [Step 5] 等待 HA 自动切换 ); await probeTask; sw.Stop(); report.RTO_Milliseconds sw.ElapsedMilliseconds; // 6. 验证 RPO在新主库查询“哨兵数据”是否存在 _logger.LogInformation( [Step 6] 验证 RPO (数据一致性) ); report.RPO_Passed await VerifySentinelDataAsync(report.NewMaster, sentinelId, ct); report.Success report.RPO_Passed report.RTO_Milliseconds _config.MaxAcceptableRTO; } catch (Exception ex) { _logger.LogError(ex, 混沌演练发生异常); report.Success false; report.ErrorMessage ex.Message; } finally { // 7. 恢复现场启动老主库让它通过 pg_rewind 自动降级为备库 _logger.LogInformation( [Step 7] 恢复现场 ); await RecoverMasterAsync(report.OriginalMaster, ct); report.EndTime DateTime.Now; } return report; } /// summary /// 持续探测 VIP 的可用性计算 RTO 的核心 /// /summary private async Task StartContinuousProbingAsync(ChaosReport report, CancellationToken ct) { int failCount 0; bool switched false; while (!ct.IsCancellationRequested !switched) { try { await using var conn new NpgsqlConnection(_config.VipConnectionString); await conn.OpenAsync(ct); await using var cmd new NpgsqlCommand(SELECT inet_server_addr(), pg_is_in_recovery(), conn); await using var reader await cmd.ExecuteReaderAsync(ct); if (await reader.ReadAsync(ct)) { var serverIp reader.GetString(0); var isInRecovery reader.GetBoolean(1); // false 表示是主库 // 如果连上了且不是恢复模式说明是主库且 IP 不是原来的主库 IP if (!isInRecovery serverIp ! report.OriginalMaster) { report.NewMaster serverIp; report.VipRecoveredAt DateTime.Now; switched true; _logger.LogInformation( VIP 已漂移到新主库: {IP}, serverIp); } } failCount 0; // 连接成功重置失败计数 } catch (Exception) { failCount; // 允许短暂的连接失败VIP 漂移期间 } await Task.Delay(100, ct); // 每 100ms 探测一次精确计算 RTO } } /// summary /// 通过 SSH 注入故障 /// /summary private async Task InjectFaultAsync(string hostIp, string command, CancellationToken ct) { await Task.Run(() { using var client new SshClient(hostIp, _config.SshUser, _config.SshPassword); client.Connect(); var result client.RunCommand(command); _logger.LogWarning(故障注入命令执行结果: {Result}, result.Result); client.Disconnect(); }, ct); } // ... 其他辅助方法 (GetCurrentMasterAsync, WriteSentinelDataAsync, etc.) 省略 ...}public class ChaosConfig{public string VipConnectionString { get; set; } “Host192.168.1.88;Port54321;Databaseprod_db;Usernamesystem;Passwordxxx;Timeout3;”;public string SshUser { get; set; } “root”;public string SshPassword { get; set; } “xxx”;public int MaxAcceptableRTO { get; set; } 30000; // 30秒}public class ChaosReport{public string DrillType { get; set; } “”;public bool Success { get; set; }public string OriginalMaster { get; set; } “”;public string NewMaster { get; set; } “”;public Guid SentinelId { get; set; }public bool RPO_Passed { get; set; }public long RTO_Milliseconds { get; set; }public DateTime StartTime { get; set; }public DateTime FaultInjectedAt { get; set; }public DateTime VipRecoveredAt { get; set; }public DateTime EndTime { get; set; }public string? ErrorMessage { get; set; }}五、性能调优与避坑指南让切换真正做到 RPO05.1 金仓高可用 10 大避坑清单血泪总结坑 后果 正确做法1 用 Keepalived 做金仓 HA 网络抖动导致双主脑裂数据写花 必须使用基于 etcd/Raft 的 kbha 或 sys_ha 组件。2 没配置同步流复制 主库宕机时备库还没收到最新 WAL导致丢数据 (RPO 0) 核心业务必须配置 synchronous_commit remote_apply。3 没使用复制槽 (Replication Slot) 备库宕机重启后主库 WAL 已清理备库只能全量重建 必须创建物理复制槽但需监控槽的积压防止撑爆主库磁盘。4 老主库恢复后直接启动 老主库带着“分叉”的数据启动导致集群状态混乱 必须开启 use_pg_rewind让老主库自动回滚分叉数据并降级为备库。5 Fencing (隔离) 没做彻底 老主库进程没死透继续接收写入 VIP 漂移脚本中必须包含 STONITH 动作如 kill -9 或 ip link set down。6 etcd 集群节点数为偶数 (如 2 或 4) 无法形成多数派脑裂风险高 etcd 集群必须是奇数节点3 或 5。7 备库延迟过大时允许提升 提升了一个落后 1GB 数据的备库导致大量数据丢失 配置 maximum_lag_on_failover延迟超标时拒绝自动切换转人工。8 应用连接池没配置重试 VIP 漂移的 5 秒内应用直接报错给用户 HikariCP / Npgsql 必须配置 Connection Timeout 和 Retry 机制。9 大事务导致同步延迟 一个 10GB 的 DELETE 导致同步卡住HA 误判主库宕机 拆分大事务调整 wal_sender_timeout 和 wal_receiver_timeout。10 没有定期做混沌演练 切换脚本因为 OS 密码过期或路径变更而失效 部署 C# 混沌演练引擎每周自动执行一次“拔网线”测试。5.2 监控与告警指标Prometheus Grafana金仓高可用核心监控指标 (通过 sys_stat 视图采集)主备延迟 (字节数) 如果 10MB触发 P2 告警如果 100MB触发 P1 告警SELECT pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytesFROM sys_stat_replication WHERE application_name ‘kb_node2’;复制槽积压 (字节数) 如果备库长期宕机复制槽会积压大量 WAL撑爆主库磁盘必须监控如果 5GB触发 P1 告警必要时手动删除槽。SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS slot_lag_bytesFROM sys_replication_slots WHERE slot_name ‘slot_node2’;检查点频率 频繁的检查点会导致 IO 毛刺影响流复制性能SELECT checkpoints_timed, checkpoints_req FROM sys_stat_bgwriter;六、总结与避坑清单6.1 墨夶的最后唠叨老铁们从 MySQL 切到人大金仓高可用架构的重构是重中之重。MySQL 的 HA 生态是“百花齐放”你可以用 MHA也可以用 Orchestrator甚至自己写脚本。但金仓PG系的高可用必须敬畏其底层的 WAL 机制和分布式一致性etcd原理。用 Keepalived 搞金仓 HA就像用透明胶带给飞机补漏——看着能飞一上天就解体。记住三句话防脑裂是高可用的底线。 抛弃 VRRP拥抱 etcd 分布式租约。RPO0 必须靠同步流复制。 synchronous_commit remote_apply 是核心业务的护身符。没有演练的 HA 就是定时炸弹。 用代码自动“拔网线”把故障消灭在演习中。
返回列表