分库分表风险应对手册(生产实战版)

包含:风险现象 → 原因 → 解决方案 → 预防措施 → 代码/配置示例,可用于架构评审、上线检查、故障应急。


1. 无分片键查询 → 全库表广播,数据库雪崩

现象

  • 接口 RT 突增几百毫秒~几秒
  • 所有分库 CPU 飙升、IO 飙升
  • 连接数瞬间打满,应用假死

根本原因

查询条件不携带分片键,Sharding 必须向所有库、所有表发送 SQL,然后内存聚合。

解决方案

  1. 强制接口必传分片键(user_id、order_id 等)
  2. 后台管理、运营查询走 ES/ClickHouse/Doris
  3. 对非核心查询做限流、熔断
  4. 对必须全表查询的场景做离线同步

预防措施

yaml 复制代码
# ShardingSphere 开启全路由告警
spring.shardingsphere.props:
  sql-show: false
  check-all-route-enabled: true  # 开启广播表检测
java 复制代码
// 代码层强制校验分片键(AOP 实现)
@Before("execution(* com.xxx.mapper.*.select*(..))")
public void checkShardingColumn() {
    Object param = getParamValue();
    if (param == null || ShardingUtil.getShardingValue(param) == null) {
        throw new RuntimeException("禁止无分片键查询");
    }
}

2. 跨库 JOIN → 应用 OOM / 极慢查询

现象

  • 简单关联查询执行几秒
  • 应用频繁 FullGC、OOM 宕机
  • 数据库线程堆积

根本原因

Sharding 不支持分布式 join,只能:

各库查 → 网络传输 → 应用内存聚合 → 性能灾难

解决方案

  1. 绝对禁止跨库 join
  2. 拆分为多次单表查询,业务层聚合
  3. 构建宽表,冗余字段避免关联
  4. 报表类走数仓

预防措施

  • CodeReview 拦截 XML 里的 JOIN
  • SQL 审计平台自动拦截
  • 固定使用单表查询规范

3. 数据倾斜(热分片)→ 分库分表失效

现象

  • 部分表数据占比 70%+
  • 热点分片查询极慢
  • 其他分片空闲浪费

根本原因

  • 分片键选择不合理(地区、商家、活动)
  • 哈希算法不均匀
  • 超级商家/大用户存在

解决方案

  1. 热点数据单独分片、单独缓存
  2. 使用 复合分片键(user_id + order_id)
  3. 对超级商家做业务层打散

预防措施

上线前执行数据分布统计:

sql 复制代码
select user_id%2 as db_idx, count(*) from t_order group by db_idx;

任何分片占比 >30% 必须整改。


4. 分布式事务不一致 → 部分成功部分失败

现象

  • 订单创建成功,库存没扣
  • 库存扣了,订单没生成
  • 资金与业务数据不一致

根本原因

分库后跨数据源,Spring 本地事务失效。

解决方案

  1. 尽量避免跨库写(同用户数据路由到同库)
  2. 引入 Seata AT 模式 分布式事务
  3. 核心场景使用 消息最终一致性(RocketMQ 事务消息)

预防措施

yaml 复制代码
# Seata 整合示例
spring:
  cloud:
    alibaba:
      seata:
        tx-service-group: my_test_tx_group
java 复制代码
@GlobalTransactional(rollbackFor = Exception.class)
public void createOrder() {
    orderMapper.insert();
    stockMapper.deduct();
}

5. 双写不一致 → 新旧库数据对不上

现象

  • 旧表有数据,分片表无
  • 分片表多数据、重复数据
  • 金额/状态不一致

根本原因

  • 双写异步失败未重试
  • 异常被吞
  • 事务未统一

解决方案

  1. 双写失败自动重试(3 次)
  2. 定时对账任务,不一致自动修复
  3. 关键路径同步双写

预防措施

java 复制代码
// 重试模板
RetryTemplate.execute(retryContext -> {
    shardingOrderMapper.insert(order);
    return true;
});
sql 复制代码
-- 每日自动对账
SELECT count(*) FROM old_table 
WHERE id NOT IN (SELECT id FROM sharding_table);

6. 数据库连接数爆炸 → 数据库拒绝连接

现象

  • 应用报 CannotGetJdbcConnection
  • 数据库 show processlist 爆满
  • 新请求进不来

根本原因

分 4 库 × 20 连接 × 10 实例 = 800 连接,MySQL 默认只有 151~3000。

解决方案

  1. 每个库连接数设为 8~16
  2. 合理评估总连接数
  3. 开启连接池监控告警

预防措施

yaml 复制代码
spring.shardingsphere.datasource.ds0.hikari:
  maximum-pool-size: 12
  minimum-idle: 3

7. 深度分页 limit m,n → OOM / 极慢

现象

  • limit 100000,10 直接卡死
  • 应用内存飙升
  • 所有分片参与查询排序

解决方案

  1. 禁止深度分页
  2. 使用 游标分页(cursor-based)
  3. 后台导出走离线任务

预防措施

java 复制代码
// 游标分页示例
lambdaQuery()
    .gt(Order::getId, lastId)
    .orderByAsc(Order::getId)
    .limit(20);

8. 分片键设计错误 → 架构级事故

现象

  • 上线后发现查询无法带分片键
  • 无法扩容
  • 无法做统计

根本原因

  • 用时间、地区、商家 ID 做分片
  • 未评估业务查询模型

解决方案

  1. 优先选择 用户 ID / 订单 ID
  2. 哈希分片,均匀分布
  3. 复合分片提高兼容性

预防措施

架构评审必须回答三问:

  1. 90% 查询是否带它?
  2. 是否均匀?
  3. 是否稳定不变?

9. DDL 风险 → 锁表、漏执行、主从延迟

现象

  • 加字段导致线上阻塞
  • 部分表加了,部分没加
  • 主从延迟巨大

解决方案

  1. 使用 gh-ost / pt-online-schema-change
  2. 低峰执行
  3. 批量自动执行,禁止手动

预防措施

编写批量执行 DDL 脚本:

bash 复制代码
for db in order_db_0 order_db_1; do
  for table in t_order_0 t_order_1 t_order_2 t_order_3; do
    mysql -e "ALTER TABLE $db.$table ADD COLUMN ..."
  done
done

10. 扩容困难(取模分片无法在线扩)

现象

user_id%4 → 想扩成 8 张表

→ 必须全量迁移、停服、改代码

解决方案

  1. 初始规划 2库16表 / 4库32表,一步到位
  2. 使用一致性哈希分片
  3. 使用 ShardingSphere 自动扩容能力

预防措施

yaml 复制代码
# 使用 MOD 避免简单取模
sharding-algorithms:
  table-sharding:
    type: MOD
    props:
      sharding-count: 16

二、生产上线前必查 10 条红线

  1. ❌ 禁止无分片键查询
  2. ❌ 禁止跨库 JOIN
  3. ❌ 禁止深度分页
  4. ❌ 禁止使用自增 ID
  5. ❌ 禁止手动分表 DDL
  6. ❌ 禁止无监控上线
  7. ❌ 禁止无双写/无回滚直接切
  8. ❌ 禁止数据倾斜
  9. ❌ 禁止无对账任务
  10. ❌ 禁止非哈希分片

三、故障应急 3 步法

  1. 切回旧数据源(双写还在,1 分钟恢复)
  2. 关闭非核心查询,限流
  3. 排查全路由 SQL / 慢 SQL / 热点分片
相关推荐
XDHCOM2 小时前
ORA-06521: PL/SQL映射函数错误,权威解析Oracle报错故障修复与远程处理方案
数据库·sql·oracle
wgzrmlrm743 小时前
mysql如何配置全文索引停用词_mysql ft_stopword_file设置
jvm·数据库·python
城数派3 小时前
2025年南京市全类别POI(55W+数据)
数据库·arcgis·信息可视化·数据分析·excel
疯狂成瘾者3 小时前
后端系统、服务稳定性里核心的指标有哪些
数据库
SPC的存折4 小时前
openEuler 24.03 MariaDB Galera 集群部署指南(cz)
linux·运维·服务器·数据库·mysql
仲芒4 小时前
[24年单独笔记] MySQL 常用的 DML 命令
数据库·笔记·mysql
SPC的存折4 小时前
MySQL 8.0 分库分表
linux·运维·服务器·数据库·mysql
蓦然乍醒4 小时前
使用 DBeaver 还原 PostgreSQL 备份文件 (.bak) 技术文档
数据库·postgresql
XDHCOM4 小时前
Redis节点故障自动恢复机制详解,如何快速抢救故障节点,确保数据不丢失?
java·数据库·redis