第4章 数据层高可用:从单点到集群的进化之路
数据层的高可用,说白了就是保证你的数据不丢、服务不断。我做了这么多年交易系统,见过太多因为数据层挂掉导致整个平台瘫痪的案例。今天咱们就聊聊这块的核心技术。
4.1 数据库主从复制:高可用的基石
MySQL的主从复制,是数据层高可用的第一步。它的原理其实不复杂:主库写binlog,从库拉取binlog并回放。
我个人习惯用异步复制,性能好。但要注意,异步复制有数据丢失的风险。我在项目中遇到过主库宕机,从库差了0.5秒的数据,结果对账怎么都对不上。
核心配置示例:
# 主库配置
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
sync-binlog = 1
# 从库配置
server-id = 2
relay-log = mysql-relay-bin
read-only = 1
log-slave-updates = 1
我的建议:交易系统建议用半同步复制。虽然性能会降一点,但数据一致性更有保障。我曾经因为省这点性能,吃了大亏。
4.2 读写分离:让数据库喘口气
读写分离,就是把查询请求打到从库,写请求走主库。你想想看,交易系统里读请求往往是写请求的10倍以上,不分离开怎么扛得住?
实现方式有两种:
- 应用层实现:代码里手动指定数据源。灵活,但侵入性强。
- 中间件实现:比如ShardingSphere、MyCat。透明,但多了一层网络开销。
嗯,这里要注意主从延迟的问题。我记得有一次,用户刚下单就查订单,结果从库还没同步到,显示订单不存在。用户直接炸了。
避坑指南:我曾经在项目中用强制路由解决这个问题。关键查询走主库,普通查询走从库。虽然麻烦点,但数据一致性有保障。
4.3 分库分表:量变引起质变
当单库数据量超过500万,或者QPS超过5000,就该考虑分库分表了。ShardingSphere是我用得最多的方案。
分片策略我一般这样选:
| 策略 | 适用场景 | 我的经验 |
|---|---|---|
| 取模分片 | 数据均匀分布 | 适合订单表,但扩容麻烦 |
| 范围分片 | 按时间查询多 | 适合日志表,容易产生热点 |
| 一致性哈希 | 需要动态扩容 | 我推荐这个,扩容影响最小 |
ShardingSphere配置示例:
# 分片规则
sharding:
tables:
t_order:
actualDataNodes: ds$->{0..1}.t_order_$->{0..1}
tableStrategy:
inline:
shardingColumn: order_id
algorithmExpression: t_order_$->{order_id % 2}
databaseStrategy:
inline:
shardingColumn: user_id
algorithmExpression: ds$->{user_id % 2}
4.4 数据库连接池:别让连接成为瓶颈
连接池优化,很多人不重视。其实这里坑特别多。我见过一个项目,连接池设了200个,结果数据库连接数被打满,整个系统都卡死了。
我个人习惯用HikariCP,性能确实好。关键参数这样调:
- maximumPoolSize:核心数*2 + 1,别贪多
- minimumIdle:和maximum一样,避免频繁创建
- connectionTimeout:3000ms,超时就快速失败
- maxLifetime:1800000ms,30分钟,避免连接泄漏
我的经验:连接池不是越大越好。我曾经把连接池从50调到200,QPS反而下降了。因为上下文切换太频繁了。
4.5 缓存策略:Redis集群与本地缓存
缓存是数据层的加速器。我一般用两级缓存:本地缓存扛热点,Redis集群扛容量。
Redis集群部署要点:
- 至少3主3从,保证高可用
- 内存不要超过总内存的70%,留点给持久化
- 开启AOF持久化,每秒刷盘一次
本地缓存我常用Caffeine:
// 本地缓存配置
Cache localCache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(5, TimeUnit.SECONDS)
.recordStats()
.build();
// 查询逻辑
Order order = localCache.get(key, k -> {
// 先查Redis,再查数据库
Order redisOrder = redisCache.get(k);
if (redisOrder != null) return redisOrder;
return orderMapper.selectById(k);
});
避坑指南:我曾经在项目中遇到缓存雪崩,Redis集群挂了,所有请求直接打到数据库。从那以后,我强制要求本地缓存必须兜底,哪怕数据旧一点,也不能让数据库挂掉。
知识体系总览
下面这张图,是我对数据层高可用架构的理解。你看一眼,基本就明白整体脉络了。
数据层高可用,说白了就是一层一层地加防护。连接池扛连接,缓存扛热点,分库分表扛容量,主从复制扛故障。每一层都做好了,系统才能稳如泰山。
公众号:蓝海资料掘金营,微信deep3321