Apache ShardingSphere 原理与实战
Apache ShardingSphere 是一个分布式 SQL 事务和数据库治理生态,常用于在不改变 MySQL 基本使用习惯的前提下实现数据分片、读写分离、分布式事务、影子库和数据加密等能力。
先记住最重要的边界:
ShardingSphere 负责解析、路由、改写、执行和归并 SQL,不会让跨分片查询变成单库查询,也不会自动替你选出正确的分片键、解决业务幂等或完成零风险迁移。
本文面向 ShardingSphere 5.x。不同小版本的依赖、配置项和 DistSQL 语法可能变化,项目落地时必须以目标版本官方文档和集成测试为准。
学习目标
学完后应能回答:
- ShardingSphere-JDBC 与 ShardingSphere-Proxy 怎么选。
- 一条 SQL 如何经过解析、绑定、路由、改写、执行和归并。
- 标准路由、绑定表、广播表、Hint 分片分别解决什么。
- SQL 没带分片键时为什么可能广播到所有分片。
- 分库、分表、读写分离和分布式 ID 如何配置。
- LOCAL、XA、BASE 事务的能力和代价。
- 如何迁移、扩容、排查路由错误和数据不一致。
- 哪些问题 ShardingSphere 解决不了。
产品形态
ShardingSphere-JDBC
它以 Java JDBC 增强层的方式嵌入应用:
flowchart LR
A["Java 应用"] --> B["ShardingSphere-JDBC"]
B --> C["ds_0"]
B --> D["ds_1"]特点:
- 应用进程内完成 SQL 解析、路由、改写和结果归并。
- 不增加一次代理网络跳转,适合 Java 项目。
- 每个应用实例都需要配置规则和数据库连接池。
- 升级依赖通常需要发布应用。
- 归并、大结果集和路由计算会消耗应用 JVM 的 CPU 与内存。
ShardingSphere-Proxy
它是独立数据库代理,对外暴露兼容 MySQL 或 PostgreSQL 的协议:
flowchart LR
A["Java / Go / Python 等应用"] --> B["ShardingSphere-Proxy"]
B --> C["ds_0"]
B --> D["ds_1"]特点:
- 多语言应用可以像连接普通数据库一样接入。
- 规则和治理集中管理,应用与分片拓扑耦合较少。
- 多一次网络和代理处理链路。
- Proxy 自身的容量、连接池、高可用和监控成为新的运维责任。
- 不是简单启动一个代理就具备高可用,至少要部署多实例并配置健康检查和流量入口。
选型对比
| 维度 | ShardingSphere-JDBC | ShardingSphere-Proxy |
|---|---|---|
| 接入语言 | Java | 多语言 |
| 部署位置 | 应用进程内 | 独立代理进程 |
| 网络链路 | 应用直接连接数据库 | 应用先连接 Proxy |
| 规则管理 | 随应用配置或治理中心 | Proxy 集中配置,可配合 DistSQL |
| 连接池 | 每个应用实例维护各分片连接池 | Proxy 统一维护后端连接池 |
| 扩容重点 | 应用 JVM 与连接总量 | Proxy 集群与后端连接容量 |
| 升级方式 | 通常随应用发布 | 可独立升级,但要验证协议和 SQL 兼容性 |
| 典型场景 | 单一 Java 技术栈、追求低延迟 | 多语言、遗留系统、集中治理 |
一个项目不要因为“Proxy 更像数据库”就默认选择 Proxy,也不要因为 Java 项目就无条件选 JDBC。要比较语言、发布独立性、连接规模、治理方式、故障域和团队运维能力。
核心执行链路
一条逻辑 SQL 通常经过下面的内核阶段:
flowchart TD
A["逻辑 SQL"] --> B["SQL 解析"]
B --> C["绑定与元数据识别"]
C --> D["路由"]
D --> E["SQL 改写"]
E --> F["并发执行"]
F --> G["结果归并"]
G --> H["返回应用"]1. SQL 解析
将 SQL 转换为抽象语法结构,识别逻辑表、列、条件、排序、聚合、分页和参数位置。SQL 能被 MySQL 执行,不代表一定被当前 ShardingSphere 版本完整支持。
2. 绑定
结合元数据识别表、字段、分片规则以及条件含义,为路由和改写准备上下文。这里的“绑定”不是绑定表配置;绑定表是后面减少关联路由组合的一种规则。
3. 路由
根据分片键值和算法计算目标数据源、真实表。可能是单分片路由,也可能是多个甚至全部分片。
4. SQL 改写
把逻辑表名改成真实表名,并根据分页、聚合等场景补充归并所需内容。例如逻辑表 t_order 可能被改写为 ds_0.t_order_2。
5. 执行
管理到真实数据源的连接,串行或并发执行多条真实 SQL。分片越多,连接、线程、网络和数据库负载越高。
6. 归并
将多个分片返回的结果做遍历、排序、分组、聚合或分页归并。order by、group by、distinct 和深分页都可能显著增加内存与计算成本。
路由类型
单分片路由
select *
from t_order
where user_id = 1001
and order_id = 90001;若规则能从 user_id、order_id 精准算出库表,只访问一个真实节点,成本最可控。
多分片路由
select *
from t_order
where user_id in (1001, 1002, 1003);不同值可能路由到多个节点,再归并结果。不是错误,但要控制分片数量和返回量。
全路由
select *
from t_order
where status = 'PAID';如果 status 不是分片键,而且没有其他可路由条件,通常需要访问该逻辑表的所有真实节点。分片数越多,放大越严重。
范围路由
select *
from t_order
where user_id between 1000 and 5000;能否缩小节点取决于算法是否实现范围路由。简单 Inline 取模表达式通常不能仅凭一个范围准确缩小到少数节点,可能退化为全路由。不要只验证等值查询。
Hint 路由
当 SQL 中没有分片键,但上游上下文明确知道目标分片时,可以使用 Hint。典型场景是按租户路由,但某些 SQL 没有显式携带租户列。
Hint 的风险:
- 路由值来自线程上下文,容易与 SQL 参数脱节。
- 线程池复用时必须在
finally或 try-with-resources 中清理。 - 异步任务、响应式编程和跨线程调用不能默认继承 ThreadLocal。
- Hint 错误可能查询不到数据或写入错误分片。
分片规则中的核心对象
| 对象 | 作用 | 典型例子 |
|---|---|---|
| 逻辑表 | 应用 SQL 中使用的表 | t_order |
| 真实表 | 实际 MySQL 中的物理表 | ds_0.t_order_2 |
| Actual Data Nodes | 逻辑表对应的所有真实节点 | ds_${0..1}.t_order_${0..3} |
| 分库策略 | 决定进入哪个数据源 | user_id % 2 |
| 分表策略 | 决定进入哪张物理表 | order_id % 4 |
| 分片算法 | 根据分片值计算目标 | Inline、Standard、自定义算法 |
| 主键生成器 | 生成跨表唯一 ID | Snowflake、UUID、自定义 |
一个最小分库分表示例
下面是结构示意,表达“按用户分库、按订单分表”。属性名和驱动接入方式要按使用的 ShardingSphere 5.x 小版本校验。
dataSources:
ds_0:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://mysql-0:3306/order_db
username: app
password: ${MYSQL_DS0_PASSWORD}
ds_1:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://mysql-1:3306/order_db
username: app
password: ${MYSQL_DS1_PASSWORD}
rules:
- !SHARDING
tables:
t_order:
actualDataNodes: ds_${0..1}.t_order_${0..3}
databaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: database-inline
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: order-inline
keyGenerateStrategy:
column: order_id
keyGeneratorName: snowflake
t_order_item:
actualDataNodes: ds_${0..1}.t_order_item_${0..3}
databaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: database-inline
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: order-item-inline
bindingTables:
- t_order,t_order_item
broadcastTables:
- t_region
shardingAlgorithms:
database-inline:
type: INLINE
props:
algorithm-expression: ds_${user_id % 2}
order-inline:
type: INLINE
props:
algorithm-expression: t_order_${order_id % 4}
order-item-inline:
type: INLINE
props:
algorithm-expression: t_order_item_${order_id % 4}
keyGenerators:
snowflake:
type: SNOWFLAKE
props:
sql-show: false生产注意:
- 不要把数据库密码直接写进仓库,示例中的环境变量仍应由密钥系统注入。
- SQL 展示日志在排查时有帮助,但长期打开可能产生大量日志并泄露敏感参数。
- 两个数据源各有连接池。若有 20 个应用实例、每个分片池上限为 20,仅两个分片理论上就可能创建 800 个连接。
%取模只适用于明确的均匀键,不能假定业务 ID 天然均匀。
Spring Boot 怎么接入
ShardingSphere 5.x 不同小版本的 Spring Boot Starter 和配置方式有过调整。更稳妥的理解是:应用最终需要获得一个 ShardingSphere DataSource,MyBatis、JPA 或 JDBC 模板只连接这个逻辑数据源。
常见方式包括:
- 使用 ShardingSphereDriver,通过
jdbc:shardingsphere:classpath:...加载规则。 - 使用目标版本提供的 Spring Boot 集成依赖。
- 使用 YAML 工厂或 Java API 显式创建
DataSourceBean。 - 应用连接独立的 ShardingSphere-Proxy,此时应用侧使用普通 MySQL 驱动。
不要从旧文章直接复制 spring.shardingsphere.* 配置后就升级版本。必须锁定依赖版本、核对属性名、启动时打印实际规则,并用集成测试验证路由结果。
业务代码原则上仍然操作逻辑表:
@Transactional
public void createOrder(Order order) {
orderMapper.insertOrder(order);
orderItemMapper.batchInsert(order.getItems());
}关键是 order 和 order_item 使用相同分片值,保证路由到同库同后缀表,事务才能尽量保持在单分片本地事务内。
分片算法怎么选
Standard
单个分片键,通常同时支持精确值和范围值。最常用,也最容易治理。
Complex
根据多个分片列共同路由,例如 tenant_id + created_at。组合算法会增加 SQL 条件要求和扩容复杂度,不能为了“更均匀”随意采用。
Hint
分片值不来自 SQL,而来自外部上下文。适合特殊场景,不应成为所有查询漏传分片键的补丁。
Inline
用表达式快速实现简单精确路由,例如取模。优点是配置简单;缺点是复杂范围、动态映射和扩容场景能力有限。
Class Based 或自定义 SPI
业务确实需要自定义算法时使用。算法必须做到:
- 同一分片值永远得到确定结果。
- 精确与范围路由语义一致。
- 空值、类型转换和异常有明确处理。
- 新旧规则能支持迁移期并存。
- 单元测试覆盖边界值,集成测试验证真实节点。
分片键设计
以订单系统为例:
| 查询 | 用 user_id 分片 | 用 order_id 分片 |
|---|---|---|
| 用户订单列表 | 单分片 | 如果无法反推用户则广播或需路由表 |
| 订单 ID 详情 | 需要同时带用户 ID 或路由表 | 可精准路由 |
| 商家订单列表 | 跨用户,可能广播 | 仍可能广播 |
| 按时间统计全站订单 | 全分片聚合 | 全分片聚合 |
不存在对所有查询都完美的分片键。正确做法是确定最核心的写入和查询路径,让高频事务单分片;全局查询交给搜索索引、宽表、CDC、数仓或 OLAP,而不是强迫分片数据库承担所有访问模型。
绑定表为什么重要
订单表和订单明细表使用相同的分片规则,并声明为绑定表:
select o.order_id, i.sku_id
from t_order o
join t_order_item i on i.order_id = o.order_id
where o.user_id = 1001
and o.order_id = 90001;中间件可以将相同后缀的真实表配对执行,例如:
ds_1.t_order_1 join ds_1.t_order_item_1如果规则一致但没有声明绑定关系,路由器可能无法利用这种关系,产生更多真实表组合。绑定表不是普通外键,它要求分片策略、分片值和真实节点拓扑能够对应。
广播表
广播表在每个数据源保存相同数据,适合区域、字典和少量配置:
- 与分片表 Join 时可以在目标库本地完成。
- 表必须小、更新低频。
- 更新需要传播到所有数据源,部分失败会产生不一致。
- 高并发写表、大表和强实时频繁变更表不适合广播。
分布式主键
ShardingSphere 可以配置 Snowflake 等键生成器,但生成 ID 只是第一步。还要考虑:
- worker 标识是否在所有实例唯一。
- 时钟回拨时的行为。
- ID 趋势递增但不严格连续。
- 业务是否把 ID 暴露为可枚举资源。
- 扩缩容和容器重建后 worker 配置是否冲突。
- 如果表按 ID 取模,生成算法低位分布是否满足均匀性。
不要用 select max(id) + 1 生成分布式主键,它在并发下不能保证唯一。
读写分离
读写分离规则通常把写入和事务内关键读取路由到主库,将普通只读请求分配到只读副本。
它不能消除 MySQL 复制延迟:
应用写主库成功 → binlog 传输 → 副本重放 → 从库才可见写后立即读的处理策略包括:
- 强一致读取显式走主库。
- 同一业务会话在写后窗口内读主库。
- 结合 GTID 或复制位点等待副本追上。
- 延迟超过阈值时摘除副本。
- 用版本号和状态机让业务识别旧数据。
负载均衡算法只决定“选哪个副本”,不提供复制一致性。事务中的查询究竟如何路由还受事务类型和版本规则影响,必须集成测试。
分布式事务
LOCAL
本地事务适合所有读写路由到同一真实数据源的场景,性能和语义最简单。跨多个真实数据源时,普通本地事务无法保证整体原子性。
最佳设计不是先选 XA,而是优先让一次核心事务的数据具有相同分片键,落在同一分片。
XA
通过两阶段提交协调多个数据库资源,适合必须强一致、参与者有限的场景。代价包括:
- 更多网络往返和日志。
- 资源锁持有更久。
- 协调器和参与者故障恢复更复杂。
- 吞吐与可用性下降。
XA 不会自动解决外部 HTTP、消息、文件系统等非 XA 资源的一致性。
BASE
通常通过柔性事务框架追求最终一致性,例如与 Seata 等方案集成。需要业务具备幂等、重试、补偿、悬挂和空回滚处理能力。可用性更高不意味着业务实现更简单。
事务选型顺序
- 调整数据模型,让事务单分片,使用 LOCAL。
- 能异步完成的跨分片动作使用 Outbox、事务消息、幂等和补偿。
- 确实要求跨库强一致时再评估 XA。
- 选择后用故障注入测试协调器、数据库、网络和应用宕机场景。
跨分片 Join、聚合与分页
Join
优先支持相同分片键、相同路由结果的绑定表 Join。任意跨库大表 Join 成本高,部分 SQL 形态还可能受支持范围限制。常用替代方式:
- 冗余必要字段。
- 广播小维表。
- 先批量查询一侧 ID,再按分片批量查询。
- 构建搜索或查询宽表。
- 同步到数仓或 OLAP。
聚合
例如全局 sum(amount),各分片先算局部结果,中间件再归并。avg 不能简单平均各分片平均值,需要用总和与总数重新计算。聚合列、分组键和排序组合越复杂,归并成本越高。
深分页
select *
from t_order
order by created_at desc
limit 100000, 20;多个分片可能各自读取大量候选行,再全局归并并丢弃前 100000 行。优化方向:
- 请求必须带分片键,尽量单分片分页。
- 使用
created_at + order_id的 Seek 分页。 - 全局搜索列表使用独立查询模型。
- 限制最大可翻页深度。
影子库压测
ShardingSphere 的影子规则可以根据 SQL 特征或 Hint 将压测流量路由到影子数据源,避免污染生产业务数据。
需要保证:
- 所有入口都携带不可伪造的压测标识。
- 缓存、消息、搜索、对象存储等下游也完成隔离。
- 影子库结构和数据规模能代表生产。
- 压测标识不会被普通用户构造。
只隔离数据库但没有隔离消息和外部副作用,仍可能产生真实通知、扣款或任务。
数据加密与脱敏边界
ShardingSphere 可通过规则对逻辑列与明文/密文列进行改写,但仍需解决:
- 密钥存储和轮换。
- 老数据迁移和双列过渡。
- 模糊查询、排序和索引能力下降。
- 日志、备份和下游同步是否泄露明文。
- 管理员、Proxy 与应用分别能看到什么。
中间件加密不能替代访问控制、TLS、备份加密和数据治理。
DistSQL 与治理
ShardingSphere-Proxy 可以使用 DistSQL 管理数据源和规则。它适合动态治理与运维自动化,但生产执行仍要:
- 对目标版本语法做验证。
- 使用最小权限管理账号。
- 变更前导出规则快照。
- 审批、审计、灰度并准备回滚。
- 防止多个管理入口同时修改配置。
- 检查规则是否已在所有 Proxy 实例生效。
不要在事故现场凭记忆执行 DistSQL 修改拓扑。
从单表迁移到分片表
一个安全迁移过程通常包括:
flowchart TD
A["确定新规则并压测"] --> B["创建真实库表"]
B --> C["迁移历史全量数据"]
C --> D["同步增量变更"]
D --> E["按主键/行数/校验和核对"]
E --> F["灰度切换读取"]
F --> G["切换写入并观察"]
G --> H["保留回滚窗口"]核心问题:
- 迁移期间新写入如何不漏。
- 更新和删除如何按顺序同步。
- 双写一边成功一边失败如何补偿。
- 校验不只看总行数,还要按分片范围和业务字段核对。
- 切换期间旧应用是否仍可能写旧表。
- 回滚后增量如何反向补回。
ShardingSphere 提供的迁移或扩缩容能力会随版本演进,但工具不能替代业务停写窗口、数据校验、灰度与回滚设计。
扩容为什么不能直接改取模数
原规则:
ds = user_id % 2直接改成:
ds = user_id % 4大量已有用户的计算结果会变化,但历史数据还在旧分片,造成查不到或重复。扩容需要新旧路由并存、数据搬迁、增量同步、校验和切换。
减少数据搬迁可以考虑一致性哈希、虚拟节点、范围映射或路由表,但每种方案都会增加路由状态和运维复杂度。不要为了未来可能扩容就过早实现复杂算法。
生产监控
至少覆盖:
| 层次 | 指标 |
|---|---|
| 应用或 Proxy | QPS、P95/P99、错误率、线程、堆内存、GC |
| 路由 | 单分片/多分片/全路由比例、实际 SQL 数量、路由失败 |
| 连接池 | 每个数据源活跃、空闲、等待、超时连接数 |
| MySQL | QPS、锁、慢 SQL、Buffer Pool、CPU、IO、磁盘 |
| 复制 | 延迟、复制线程状态、错误和位点 |
| 数据质量 | 分片行数、业务校验、双写失败、补偿积压 |
如果只能看到逻辑 SQL,看不到真实路由节点和改写后的 SQL,分片问题很难排查。SQL 日志应采样、脱敏并控制量。
故障排查流程
查不到数据
- 确认逻辑 SQL 的分片值、类型和是否为 NULL。
- 用相同算法手工计算目标库表。
- 查看路由日志和真实 SQL。
- 直接查询目标真实表确认数据位置。
- 检查写入时和读取时是否使用了不同规则版本。
- 检查迁移、双写和 Hint 上下文。
同一主键出现重复数据
- 检查主键生成器 worker 是否冲突。
- 检查业务唯一键是否只在单个真实表内生效。
- 检查重试是否幂等。
- 检查迁移双写与补偿是否重复执行。
- 检查同一分片值是否被不同算法路由。
查询突然变慢
- 比较是否从单分片退化为多分片或全路由。
- 查看真实 SQL 数量、各分片耗时和归并耗时。
- 检查新增的排序、聚合、深分页和大结果集。
- 检查各数据源连接池和单个热点分片。
- 对真实表 SQL 分别执行
EXPLAIN ANALYZE。
Proxy 连接耗尽
应用到 Proxy 和 Proxy 到 MySQL 是两层连接。检查应用连接池总量、Proxy 前端连接、每个后端池上限、慢 SQL、事务和锁等待,不能只放大其中一层。
常见坑
| 坑 | 后果 | 正确方向 |
|---|---|---|
| 查询不带分片键 | 广播路由,分片越多越慢 | API 和数据模型强制携带路由键 |
| 用低区分度字段分片 | 热点或严重不均 | 以真实分布压测算法 |
| 直接修改取模分片数 | 历史数据路由错误 | 新旧规则、迁移、校验、灰度 |
| 把所有表都分片 | 开发和运维复杂度暴涨 | 只拆容量或吞吐成为瓶颈的大表 |
| 业务唯一键不含路由能力 | 无法全局约束或点查 | 路由表、全局 ID、幂等与校验 |
| 跨分片深分页 | 各分片大量读取再归并 | 单分片或 Seek 分页、独立查询模型 |
| 把读写分离当强一致 | 写后立即读到旧数据 | 强一致读主或位点等待 |
| Hint 未清理 | 线程复用后路由串租户 | 严格作用域关闭并测试跨线程 |
| SQL 日志长期全开 | IO、磁盘和敏感数据风险 | 短时、采样、脱敏 |
| 每个服务池配置过大 | 所有分片连接总数压垮 MySQL | 按实例数 × 分片数统一预算 |
以为 @Transactional 自动跨库 | 部分分片提交、部分失败 | 单分片、本地消息、XA/BASE 选型 |
| 只测正常 SQL | 上线后范围、NULL、批量、子查询失败 | 建立 SQL 兼容与路由集成测试集 |
测试策略
路由单元测试
对算法覆盖:
- 最小值、最大值、负数、NULL、字符串与数值转换。
- 等值、IN、BETWEEN 和范围边界。
- 新旧规则是否产生预期节点。
- 数据分布是否均匀。
SQL 集成测试
使用与生产相同大版本的 MySQL 和 ShardingSphere,验证:
- 单表 CRUD、批量写入和生成主键回填。
- Join、子查询、聚合、排序、分页和锁定读取。
- 事务提交、回滚、死锁重试和故障注入。
- 读写分离下的写后读。
- MyBatis/JPA 生成的真实 SQL,而不只测试手写 SQL。
路由断言
测试不仅断言业务结果,还要断言访问了哪些真实节点、产生多少条真实 SQL。结果正确但全路由的测试仍然不合格。
高频面试题
ShardingSphere-JDBC 和 Proxy 有什么区别
JDBC 嵌入 Java 应用,在进程内解析、路由和归并,网络链路短,但每个应用实例维护规则和各分片连接池;Proxy 是独立数据库代理,支持多语言和集中治理,但增加网络跳转,也需要单独解决代理容量、高可用和后端连接池问题。选型取决于语言、发布、治理和故障域,而不是谁绝对更快。
一条 SQL 在 ShardingSphere 中怎么执行
先解析 SQL 并结合元数据绑定逻辑表和条件,然后根据分片键与算法路由到真实节点;把逻辑表名和分页等内容改写成真实 SQL,并发或串行执行后,再对多个结果做遍历、排序、分组、聚合或分页归并。没有分片键时可能全路由,性能成本会随分片数放大。
绑定表和广播表有什么区别
绑定表是使用相同分片规则、能够按对应真实表配对 Join 的一组分片表,例如订单和订单明细;广播表是在每个数据源都保存一份相同数据的小表,例如地区字典,用于在各分片本地 Join。绑定表减少路由组合,广播表避免跨库读取小维表。
ShardingSphere 如何保证分布式事务
单分片优先使用 LOCAL 本地事务;跨分片强一致可以评估 XA,但会增加协调、锁持有和故障恢复成本;最终一致场景可用 BASE、事务消息、Outbox 和补偿。@Transactional 本身不能让多个独立 MySQL 本地事务自动原子提交,最佳方案仍是通过分片键让核心事务落在同一分片。
不带分片键会怎样
如果无法从 SQL 条件或 Hint 得到路由值,通常要访问所有可能的真实节点,再归并结果。它未必立即报错,却会让数据库请求数、连接占用、网络数据量和归并成本随分片数增加,是最常见的隐性性能问题。
ShardingSphere 能自动扩容吗
它可以提供规则治理和版本相关的数据迁移能力,但扩容仍要解决新旧路由并存、历史全量迁移、增量同步、双写失败、数据校验、灰度切换和回滚。直接把 % 2 改成 % 4 会让大量历史数据路由位置改变,不能称为安全扩容。
项目里如何证明 ShardingSphere 配置正确
不能只看应用启动成功。需要用真实 MySQL 做路由集成测试,对每类核心 SQL 断言真实节点和真实 SQL 数量;用分布统计验证均匀性;测试事务回滚、写后读、范围条件、批量 SQL 和异常重试;上线后监控全路由比例、连接池、各分片延迟和数据校验。
项目回答模板
我们先根据订单系统的访问模型选择分片键,高频的用户订单列表和订单写入都带 user_id,所以按 user_id 分库,让订单和明细作为绑定表使用同一分片规则,核心创建事务保持单分片 LOCAL 事务。订单 ID 使用分布式生成器,同时维护订单号到分片的路由能力,避免只按订单号查询时广播。全局搜索和报表不直接扫所有分片,而是通过 CDC 构建查询模型。接入层根据技术栈选择 ShardingSphere-JDBC 或 Proxy,并对核心 SQL 断言真实路由。上线监控全路由比例、各分片连接池、热点、复制延迟和数据校验。扩容时使用新旧规则并存、全量加增量迁移、校验、灰度切读和回滚窗口,不直接修改取模数。关联知识点
- MySQL 分库分表:分片键、跨分片事务、全局 ID 和扩容的通用原理。
- MySQL 事务:本地事务、锁和隔离级别。
- MySQL 高可用:复制、故障切换、脑裂和 RPO/RTO。
- MySQL 监控与故障排查:连接、慢 SQL、锁和资源证据链。
- 分布式事务:XA、TCC、Saga、消息与最终一致性。
- 分片与一致性哈希:取模、一致性哈希和扩容迁移。
本章小结
ShardingSphere 的价值是把逻辑 SQL 转换为对真实库表的访问,并提供分片、读写分离、事务和治理能力。真正决定系统是否可用的仍是分片键、事务边界、查询模型、连接容量、数据迁移和可观测性。面试时不要只背配置,要沿着“解析、路由、改写、执行、归并”解释运行原理,再说明广播查询、跨分片事务、深分页、复制延迟和扩容的代价。
