高可用读写分离实战(二):我把数据库主库停了,结果整个集群的反应和我想象的不一样

高可用读写分离实战(二):我把数据库主库停了,结果整个集群的反应和我想象的不一样

📌 上一篇把读写分离集群搭好了,本来只是想验证一下故障切换,没想到最后连续做了两个多小时实验。从主库宕机、VIP漂移,到读写分离和SQL路由,我把整个过程都记录了下来。这篇不是官方文档,而是一次真实的实验记录。


"要不直接把主库停了试试?"

上一篇部署完成以后,我一直觉得心里没底。

虽然 repmgr cluster show 显示一切正常,但总感觉少了点什么。

毕竟生产环境不会因为你部署成功,就真的万事大吉。

晚上九点多,我重新登录两台服务器,打开了三个终端。

一个窗口盯数据库日志。

一个窗口不停执行SQL。

还有一个窗口一直刷新集群状态。

整个环境大概长这样。

text 复制代码
                应用系统
                    │
            JDBC读写分离驱动
                    │
          ┌────────────────┐
          │      VIP       │
          └────────────────┘
              │        │
              │        │
      ┌───────┘        │
      ▼                ▼
┌────────────┐   Streaming WAL   ┌────────────┐
│ Primary    │ ───────────────▶ │ Standby    │
│ node1      │                  │ node2      │
└────────────┘                  └────────────┘

当时脑子里只有一个问题。

如果现在把 Primary 干掉,业务到底会不会中断?


我按下回车以后,数据库居然一点反应都没有

直接停止数据库。

bash 复制代码
sys_monitor.sh stop

执行完成以后,我第一时间不是看日志,而是刷新业务页面。

结果页面还能打开。

又执行了一条SQL。

sql 复制代码
SELECT NOW();

返回正常。

我当时第一反应就是:

"不会吧?是不是我停错数据库了?"

赶紧去服务器确认。

bash 复制代码
ps -ef | grep kingbase

数据库进程已经没有了。

说明主库确实停了。

但是业务为什么还能访问?

我盯着日志看了几秒。

没有切换。

没有漂移。

什么事情都没有发生。

那几秒钟,我甚至怀疑自己的高可用配置是不是有问题。


后来看日志,我才知道数据库比我冷静得多

过了几秒以后。

日志终于开始刷新。

内容类似下面这样。

text 复制代码
Reconnect to primary...

retry 1...

retry 2...

retry 3...

看到这里,我一下子明白了。

原来数据库根本不会因为一次连接失败就立刻升主。

它第一件事情不是切换。

而是确认。

Primary到底是真的挂了,还是只是网络抖了一下?

这一点我觉得设计得挺合理。

如果网络偶尔丢两个包就开始Failover,那整个集群一天不知道要切多少次。

所以生产环境里,"慢一点"反而是一种保护。

💡 我后来还专门去调整了 reconnect_attempts 参数,想让切换快一点。

测试环境确实舒服。

但想想生产环境,最后还是改回来了。

切得太快,有时候反而不是好事。


真正开始切换的时候,比想象中复杂

确认主库无法恢复以后,集群才真正进入切换流程。

我后来按照日志,大概整理了一下整个过程。

text 复制代码
Primary异常
      │
      ▼
持续重连确认
      │
      ▼
停止接收新的WAL
      │
      ▼
Standby结束Recovery
      │
      ▼
提升为Primary
      │
      ▼
VIP漂移
      │
      ▼
业务重新连接

以前一直觉得,高可用就是一句话:

主挂了,备升主。

真正看完日志以后才发现,中间还有很多细节。

尤其是WAL同步。

如果最后几条日志还没有同步完成,就直接升主,很容易出现数据不一致。

所以真正的数据库,比我们想象得谨慎得多。


我最关心的,其实是VIP

数据库什么时候升主,我其实没那么关心。

我真正关心的是:

Java程序还能不能连?

因为线上配置的数据库地址一直都是VIP。

properties 复制代码
jdbc:kingbase://192.168.10.100:54321/test

整个业务根本不知道后面是哪台服务器。

于是我登录两台机器。

执行:

bash 复制代码
ip addr

发现VIP已经从node1消失。

过了几秒。

出现在node2。

整个过程基本不用人工参与。

这也是为什么业务始终连接同一个地址,却能够自动恢复。

第一次看到VIP漂移的时候,我终于理解以前为什么很多项目数据库切换以后,Java服务根本不用改配置。


实验做到这里,我突然想到另一个问题

业务恢复以后,我本来准备结束。

结果脑子里突然冒出来一个问题。

现在新的Primary已经起来了。
那我执行一条SELECT,到底是谁在处理?

于是我没有停。

继续做实验。

打开两个数据库终端。

不停执行下面这条SQL。

sql 复制代码
SELECT inet_server_addr();

结果还真发现了点东西。

查询请求已经开始分散到不同节点。

也就是说。

读写分离已经开始工作了。


顺手做了一次小压测

没有用特别复杂的工具。

直接写了一个简单测试程序。

循环执行查询。

java 复制代码
for (int i = 0; i < 10000; i++) {
    jdbcTemplate.queryForObject(
        "select now()",
        Timestamp.class
    );
}

同时观察两个节点。

发现一个很有意思的现象。

开始的时候。

所有连接都集中在Primary。

调整JDBC读写分离配置以后。

Standby开始承担越来越多查询。

CPU也慢慢降下来了。

虽然只是一个简单测试。

但至少证明了一件事。

备库终于不是"摆设"了。

以前它只是负责同步。

现在真正开始承担查询压力。


我还踩了一个特别低级的坑

这里提醒一下。

我第一次压测一直没成功。

所有查询还是跑Primary。

我还怀疑是不是数据库配置有问题。

后来排查半天。

发现居然是JDBC URL写错了。

驱动根本没有启用读写分离模式。

所以看似数据库没生效,其实问题出在客户端。

这种问题在线上其实挺常见。

很多时候不是数据库不会工作,而是客户端根本没按预期连接。


我的一点体会

做完这次实验,我最大的感受就是:

很多人理解高可用,都是站在数据库角度。

但真正上线以后,用户根本不关心数据库。

他们只关心:

  • 页面有没有打不开?
  • SQL有没有报错?
  • 订单有没有丢?
  • 查询是不是变快了?

所以我现在做高可用测试,都会多验证几件事。

✅ JDBC连接池有没有恢复?

✅ VIP是不是正常漂移?

✅ 查询是不是已经进入Standby?

✅ 主备数据有没有延迟?

这些看起来不起眼,却比"数据库Running"更重要。


写在最后

这次实验,本来只是想验证一下Failover。

结果最后把读写分离、VIP漂移、SQL路由也一起测试了。

整个过程下来,我反而觉得,高可用真正难的地方不是部署,而是验证。

因为只有亲手做一次故障演练,你才知道自己的集群到底有没有准备好面对真正的生产环境。

下一篇,我准备把这次压测的数据整理出来,重点聊聊读写分离到底能提升多少性能、哪些SQL不会走备库,以及生产环境如何选择同步或异步复制。这部分也是我觉得最值得深入研究的内容。

相关推荐
掘金者阿豪1 小时前
《高可用读写分离集群实战》系列(一)
后端
Dilee1 小时前
Spring AI 2.0.0 Prompt 最小 Demo:system、user、template 到底怎么分工
后端
未秃头的程序猿2 小时前
Java 26正式发布!这3个新特性,让代码量直接减半
java·后端·面试
小旭Coding2 小时前
卧靠!Go 传给前端的 int64 竟然变成了这个?
后端
用户298698530142 小时前
Word 文档文本查找与替换的 Java 实现方案
java·后端
kunge20132 小时前
深度剖析Claude Code 的CLAUDE.md加载逻辑
后端·vibecoding
米沙AI2 小时前
MSYS2 快速使用版本
后端