数据库单点是最大隐患
应用服务挂了可以靠多副本顶住,但数据库是整个系统里最难搞高可用的一环——它有状态,数据就一份,主库一旦挂了、或者磁盘坏了,整个系统读写全停。很多系统做了一堆负载均衡、多副本,却把数据库孤零零跑在一台机器上,等于前面都修好了,地基还塌着。
数据库高可用的基本思路是:搞一个主库写、多个从库同步复制,主库挂了把其中一个从库升成新主。这一篇就从这个思路讲起,一步步落到能跑的最小方案。
应用服务多副本是为了扛流量;数据库主从是为了扛故障,目的不一样。
- 主从复制主库的变更自动同步到从库。
- 读写分离读走从库、写走主库,分摊压力。
- 故障切换主库挂了,把从库自动升成新主。
- 脑裂两个主同时写,数据就乱了,必须避免。
主从复制:数据怎么同步到从库
主从复制的原理说穿了很简单:主库把每一次数据变更记到日志里,从库盯着这份日志、把变更重演一遍。这样从库就有了和主库一样的数据(理论上)。
MySQL 主从复制的关键步骤:
1. 主库开启 binlog,记录所有写操作
2. 从库配好主库地址和复制账号
3. 从库拉取主库 binlog,在本地重演
4. 网络延迟决定了从库数据比主库"新到什么程度"
要记住一个现实:复制是异步的,从库数据会比主库稍微旧一点点。在网络正常时这个延迟通常毫秒级,但网络抖一下可能就几秒。这一点直接决定了下面读写分离能怎么做、不能怎么做。
读写分离:读流量分流到从库
大多数业务的特点是读多写少——一次写、可能读十次。主从搭好后,把读请求分流到从库,主库就只扛写,压力立刻小一大截。这就是读写分离:
读写分离的路由规则:
- 写请求(insert/update/delete)→ 走主库
- 读请求(select)→ 走从库
- 刚写完立刻要读的场景 → 仍走主库(不然读不到刚写的数据)
最后那条特别容易踩坑:用户刚下单、马上去看订单页,如果读的是从库,可能看到空的(复制还没同步过来)。所以「写完立刻读」的场景要特殊处理,让它读主库,别傻傻全分流。
故障切换:主库挂了怎么办
主库真的挂了(机器宕机、磁盘损坏),就得把一台从库升成新主。手动做一遍要好几分钟,业务停好几分钟——所以一般用自动故障切换工具盯着,主库一断自动把某个从库提上来:
自动切换的大致过程:
1. 健康检查发现主库连不上
2. 等一小段时间(确认不是网络抖动)
3. 选一个数据最新的从库
4. 把它提升为新主库
5. 让其他从库重新指向新主
6. 通知应用层切换连接
这套流程别自己从零写,用现成的高可用组件。它们已经把「选谁提升」「怎么防误切」这些坑趟过了。
切换后:连接串和数据一致性
切换完了,应用层怎么知道新主库地址变了?最常见的做法是应用不直连数据库 IP,而是连一个固定的虚拟地址——切换工具把这个虚拟地址指到新主。这样应用层不用改配置,无感切换。
还有一个要接受的现实:切换瞬间,主库上还没同步到从库的那一小段数据,可能丢。因为异步复制,挂掉的瞬间那点变更没来得及传过来。对绝大多数业务这可接受,但涉及钱的场景要专门设计——这也是为什么金融级场景会用更强的同步复制,代价是性能更低。
避免脑裂
故障切换最危险的事故叫脑裂:网络一断,应用还在跟老主库通信,切换工具又把另一个从库升成新主——结果变成两个主库同时在接受写请求,两边数据各写各的,彻底乱套,事后都不知道以哪边为准。
防脑裂的核心思路是:确保任何时候只有一个主。常见做法是用一个多数派仲裁——挂掉的老主库要被强制隔离掉、不让它再接受写。自动切换工具一般都内置了这个保护,关键是别把它配成「网络抖一下就盲目切换」,要留好确认窗口。
一套最小高可用方案
如果你现在数据库还是单点,按这个最小路径搭起来:
第一步:搭一个从库
- 开主从复制,确认从库数据和主库一致
第二步:读流量切到从库
- 先只切非关键的读,观察复制延迟
第三步:接自动切换组件
- 配好健康检查和切换策略
- 在测试环境演练一次:手动停主库,看能不能自动切
第四步:配虚拟地址
- 应用连固定地址,切换时不用改配置
每季度:演练一次切换
- 真演练才知道出了事能不能切成功
数据库高可用不是一朝一夕的事,但「先有一个从库做备份」这一步价值就已经很大——至少主库挂了数据还在。别追求一步到位的金融级方案,先把主从复制跑起来,再慢慢加自动切换。配合前面讲的备份容灾和高可用,数据库这层的安全感就上来了。