21、数据库可靠性模式:读写分离、分库分表、主从复制、故障转移、连接池管理(HikariCP)

数据库,往往是系统里最脆弱的一环。我见过太多项目,业务代码写得漂漂亮亮,一到高并发就垮在数据库上。说白了,数据库的可靠性不是靠运气,而是靠一套组合拳。今天我们就聊聊这五个核心模式:读写分离、分库分表、主从复制、故障转移,还有连接池管理。

主从复制:高可用的地基

主从复制是后面所有模式的基础。没有它,读写分离和故障转移都是空谈。

原理其实不复杂。主库(Master)负责写操作,把变更记录到二进制日志(binlog)里。从库(Slave)拉取这些日志,在自己身上重放一遍。这样主从数据就保持同步了。

我个人习惯用异步复制,性能好。但要注意,异步复制有数据丢失的风险。主库挂了,最后几条事务可能还没传到从库。

⚠️ 我曾经踩过这个坑: 线上主库宕机,从库晋升后丢了 3 秒的数据。后来我改成了半同步复制,至少保证有一个从库收到了日志,主库才返回成功。

MySQL 8.0 配置主从复制,核心步骤就这几行:

-- 主库配置
server-id = 1
log_bin = mysql-bin
binlog_format = ROW

-- 从库配置
server-id = 2
relay_log = relay-bin
read_only = 1

-- 从库执行
CHANGE MASTER TO
  MASTER_HOST='192.168.1.10',
  MASTER_USER='repl',
  MASTER_PASSWORD='password',
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=0;
START SLAVE;

读写分离:让查询不拖累写入

你想想看,一个系统里读请求往往是写请求的几十倍。如果读写都压在同一台库上,写操作很容易被慢查询拖死。

读写分离的思路很简单:写走主库,读走从库。这样主库专心处理写入,从库分担查询压力。

实现方式有两种。一种是代码层硬编码,在 DAO 层判断 SQL 类型,动态切换数据源。另一种是用中间件,比如 ShardingSphere、MyCat。

我个人更推荐中间件方案。为什么?因为对业务代码无侵入。你想想,如果每个 DAO 方法都要手动指定走主库还是从库,那维护成本太高了。

核心要点:
  • 写操作(INSERT/UPDATE/DELETE)→ 主库
  • 实时性要求高的读 → 主库(避免主从延迟)
  • 普通查询 → 从库
  • 主从延迟敏感的业务,可以设置延迟阈值,超过阈值自动切回主库

分库分表:突破单库瓶颈

单库能扛的并发是有限的。当数据量到了千万级、亿级,读写分离也救不了你。这时候就得考虑分库分表了。

分库,是把数据分散到多个数据库实例。分表,是把一张大表拆成多张小表。两者可以组合使用。

分片策略很关键。我常用的策略有几种:

策略 说明 适用场景
哈希分片 对分片键取模,均匀分布 数据量均匀,无热点
范围分片 按时间、ID 范围划分 有天然范围属性,如订单表按月份分
列表分片 按枚举值划分 地区、类型等有限枚举值

嗯,这里要注意。分片键选错了,后面改起来非常痛苦。我见过一个项目用用户手机号后四位做分片键,结果某个号段的用户暴增,导致单库热点。后来他们不得不做数据迁移,折腾了两周。

分库分表后的查询,要尽量带上分片键。不带分片键的查询,会广播到所有分片,性能极差。说白了,这就是「查询路由」的问题。

💡 我的建议: 如果业务还在早期,别急着分库分表。先做读写分离,加缓存,实在扛不住了再分。过早分库分表,反而增加了复杂度。

故障转移:让宕机对用户透明

主库挂了怎么办?不能干等着 DBA 手动切换。故障转移就是要自动完成这件事。

常见的方案有 MHA、Orchestrator,还有 MySQL 8.0 自带的 InnoDB Cluster + MySQL Router。

流程大致是这样:

  1. 监控组件检测到主库不可用
  2. 从候选从库中选一个数据最新的
  3. 把选中的从库提升为新主库
  4. 其他从库重新指向新主库
  5. 应用层切换数据源连接

整个过程最好控制在 30 秒以内。超过 30 秒,用户就能感知到服务不可用了。

⚠️ 我曾经遇到过: 故障转移后,新主库的数据和旧主库不一致,导致部分数据丢失。后来我加了一步「数据补齐」的流程,在切换前先对比 binlog 位置,确保数据尽量完整。

连接池管理:HikariCP 实战

连接池这东西,看似简单,但配置不好一样出问题。我见过有人把连接池设成 200,结果数据库连接数被打满,整个服务瘫痪。

HikariCP 是目前性能最好的连接池,Spring Boot 2.0 之后默认就是它。为什么快?因为它做了很多优化:

  • 代码极简,字节码量少
  • 无锁集合实现,减少线程竞争
  • 代理模式优化,减少方法调用开销

配置上,我个人的经验是:

# 核心参数
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.max-lifetime=1800000

# 监控参数
spring.datasource.hikari.pool-name=MyPool
spring.datasource.hikari.register-mbeans=true

maximum-pool-size 设多少合适?这得看你的数据库规格和业务并发量。一般 4 核 8G 的数据库,20-30 个连接就够了。设太大反而增加上下文切换开销。

还有一个容易被忽略的参数:max-lifetime。它控制连接的最大存活时间。为什么要设这个?因为数据库防火墙或者网络设备可能会断开长时间空闲的连接。设一个合理的 max-lifetime,让连接定期重建,避免被「断连」。

避坑指南:
  • connection-timeout 不要设太长,30 秒足够。超过 30 秒拿不到连接,说明系统已经出问题了,不如快速失败。
  • idle-timeout 要小于数据库的 wait_timeout,否则空闲连接会被数据库主动断开。
  • 生产环境一定要开启 register-mbeans,方便通过 JMX 监控连接池状态。

整体架构图

下面这张图展示了这五个模式如何协同工作。你可以看到,主从复制是底层支撑,读写分离和分库分表是横向扩展手段,故障转移保证高可用,连接池管理则贯穿始终。

数据库可靠性模式架构图 应用服务 HikariCP 连接池 读写分离 / 分库分表(ShardingSphere) 主库 (Master) 写入 + 实时读 从库 1 (Slave) 读请求 从库 2 (Slave) 读请求 binlog 同步 故障转移(MHA / Orchestrator) 主库 从库 故障转移 读写分离 连接池

这五个模式不是孤立的。主从复制是基础,读写分离和分库分表解决性能问题,故障转移保证可用性,连接池管理则优化资源使用。把它们组合起来,才能构建一个真正高可靠的数据库层。

公众号:蓝海资料掘金营,微信deep3321