Skip to content

Apache ShardingSphere 原理与实战

Apache ShardingSphere 是一个分布式 SQL 事务和数据库治理生态,常用于在不改变 MySQL 基本使用习惯的前提下实现数据分片、读写分离、分布式事务、影子库和数据加密等能力。

先记住最重要的边界:

ShardingSphere 负责解析、路由、改写、执行和归并 SQL,不会让跨分片查询变成单库查询,也不会自动替你选出正确的分片键、解决业务幂等或完成零风险迁移。

本文面向 ShardingSphere 5.x。不同小版本的依赖、配置项和 DistSQL 语法可能变化,项目落地时必须以目标版本官方文档和集成测试为准。

学习目标

学完后应能回答:

  1. ShardingSphere-JDBC 与 ShardingSphere-Proxy 怎么选。
  2. 一条 SQL 如何经过解析、绑定、路由、改写、执行和归并。
  3. 标准路由、绑定表、广播表、Hint 分片分别解决什么。
  4. SQL 没带分片键时为什么可能广播到所有分片。
  5. 分库、分表、读写分离和分布式 ID 如何配置。
  6. LOCAL、XA、BASE 事务的能力和代价。
  7. 如何迁移、扩容、排查路由错误和数据不一致。
  8. 哪些问题 ShardingSphere 解决不了。

产品形态

ShardingSphere-JDBC

它以 Java JDBC 增强层的方式嵌入应用:

mermaid
flowchart LR
    A["Java 应用"] --> B["ShardingSphere-JDBC"]
    B --> C["ds_0"]
    B --> D["ds_1"]

特点:

  • 应用进程内完成 SQL 解析、路由、改写和结果归并。
  • 不增加一次代理网络跳转,适合 Java 项目。
  • 每个应用实例都需要配置规则和数据库连接池。
  • 升级依赖通常需要发布应用。
  • 归并、大结果集和路由计算会消耗应用 JVM 的 CPU 与内存。

ShardingSphere-Proxy

它是独立数据库代理,对外暴露兼容 MySQL 或 PostgreSQL 的协议:

mermaid
flowchart LR
    A["Java / Go / Python 等应用"] --> B["ShardingSphere-Proxy"]
    B --> C["ds_0"]
    B --> D["ds_1"]

特点:

  • 多语言应用可以像连接普通数据库一样接入。
  • 规则和治理集中管理,应用与分片拓扑耦合较少。
  • 多一次网络和代理处理链路。
  • Proxy 自身的容量、连接池、高可用和监控成为新的运维责任。
  • 不是简单启动一个代理就具备高可用,至少要部署多实例并配置健康检查和流量入口。

选型对比

维度ShardingSphere-JDBCShardingSphere-Proxy
接入语言Java多语言
部署位置应用进程内独立代理进程
网络链路应用直接连接数据库应用先连接 Proxy
规则管理随应用配置或治理中心Proxy 集中配置,可配合 DistSQL
连接池每个应用实例维护各分片连接池Proxy 统一维护后端连接池
扩容重点应用 JVM 与连接总量Proxy 集群与后端连接容量
升级方式通常随应用发布可独立升级,但要验证协议和 SQL 兼容性
典型场景单一 Java 技术栈、追求低延迟多语言、遗留系统、集中治理

一个项目不要因为“Proxy 更像数据库”就默认选择 Proxy,也不要因为 Java 项目就无条件选 JDBC。要比较语言、发布独立性、连接规模、治理方式、故障域和团队运维能力。

核心执行链路

一条逻辑 SQL 通常经过下面的内核阶段:

mermaid
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 bygroup bydistinct 和深分页都可能显著增加内存与计算成本。

路由类型

单分片路由

sql
select *
from t_order
where user_id = 1001
  and order_id = 90001;

若规则能从 user_idorder_id 精准算出库表,只访问一个真实节点,成本最可控。

多分片路由

sql
select *
from t_order
where user_id in (1001, 1002, 1003);

不同值可能路由到多个节点,再归并结果。不是错误,但要控制分片数量和返回量。

全路由

sql
select *
from t_order
where status = 'PAID';

如果 status 不是分片键,而且没有其他可路由条件,通常需要访问该逻辑表的所有真实节点。分片数越多,放大越严重。

范围路由

sql
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、自定义算法
主键生成器生成跨表唯一 IDSnowflake、UUID、自定义

一个最小分库分表示例

下面是结构示意,表达“按用户分库、按订单分表”。属性名和驱动接入方式要按使用的 ShardingSphere 5.x 小版本校验。

yaml
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 模板只连接这个逻辑数据源。

常见方式包括:

  1. 使用 ShardingSphereDriver,通过 jdbc:shardingsphere:classpath:... 加载规则。
  2. 使用目标版本提供的 Spring Boot 集成依赖。
  3. 使用 YAML 工厂或 Java API 显式创建 DataSource Bean。
  4. 应用连接独立的 ShardingSphere-Proxy,此时应用侧使用普通 MySQL 驱动。

不要从旧文章直接复制 spring.shardingsphere.* 配置后就升级版本。必须锁定依赖版本、核对属性名、启动时打印实际规则,并用集成测试验证路由结果。

业务代码原则上仍然操作逻辑表:

java
@Transactional
public void createOrder(Order order) {
    orderMapper.insertOrder(order);
    orderItemMapper.batchInsert(order.getItems());
}

关键是 orderorder_item 使用相同分片值,保证路由到同库同后缀表,事务才能尽量保持在单分片本地事务内。

分片算法怎么选

Standard

单个分片键,通常同时支持精确值和范围值。最常用,也最容易治理。

Complex

根据多个分片列共同路由,例如 tenant_id + created_at。组合算法会增加 SQL 条件要求和扩容复杂度,不能为了“更均匀”随意采用。

Hint

分片值不来自 SQL,而来自外部上下文。适合特殊场景,不应成为所有查询漏传分片键的补丁。

Inline

用表达式快速实现简单精确路由,例如取模。优点是配置简单;缺点是复杂范围、动态映射和扩容场景能力有限。

Class Based 或自定义 SPI

业务确实需要自定义算法时使用。算法必须做到:

  • 同一分片值永远得到确定结果。
  • 精确与范围路由语义一致。
  • 空值、类型转换和异常有明确处理。
  • 新旧规则能支持迁移期并存。
  • 单元测试覆盖边界值,集成测试验证真实节点。

分片键设计

以订单系统为例:

查询user_id 分片order_id 分片
用户订单列表单分片如果无法反推用户则广播或需路由表
订单 ID 详情需要同时带用户 ID 或路由表可精准路由
商家订单列表跨用户,可能广播仍可能广播
按时间统计全站订单全分片聚合全分片聚合

不存在对所有查询都完美的分片键。正确做法是确定最核心的写入和查询路径,让高频事务单分片;全局查询交给搜索索引、宽表、CDC、数仓或 OLAP,而不是强迫分片数据库承担所有访问模型。

绑定表为什么重要

订单表和订单明细表使用相同的分片规则,并声明为绑定表:

sql
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;

中间件可以将相同后缀的真实表配对执行,例如:

text
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 复制延迟:

text
应用写主库成功 → binlog 传输 → 副本重放 → 从库才可见

写后立即读的处理策略包括:

  • 强一致读取显式走主库。
  • 同一业务会话在写后窗口内读主库。
  • 结合 GTID 或复制位点等待副本追上。
  • 延迟超过阈值时摘除副本。
  • 用版本号和状态机让业务识别旧数据。

负载均衡算法只决定“选哪个副本”,不提供复制一致性。事务中的查询究竟如何路由还受事务类型和版本规则影响,必须集成测试。

分布式事务

LOCAL

本地事务适合所有读写路由到同一真实数据源的场景,性能和语义最简单。跨多个真实数据源时,普通本地事务无法保证整体原子性。

最佳设计不是先选 XA,而是优先让一次核心事务的数据具有相同分片键,落在同一分片。

XA

通过两阶段提交协调多个数据库资源,适合必须强一致、参与者有限的场景。代价包括:

  • 更多网络往返和日志。
  • 资源锁持有更久。
  • 协调器和参与者故障恢复更复杂。
  • 吞吐与可用性下降。

XA 不会自动解决外部 HTTP、消息、文件系统等非 XA 资源的一致性。

BASE

通常通过柔性事务框架追求最终一致性,例如与 Seata 等方案集成。需要业务具备幂等、重试、补偿、悬挂和空回滚处理能力。可用性更高不意味着业务实现更简单。

事务选型顺序

  1. 调整数据模型,让事务单分片,使用 LOCAL。
  2. 能异步完成的跨分片动作使用 Outbox、事务消息、幂等和补偿。
  3. 确实要求跨库强一致时再评估 XA。
  4. 选择后用故障注入测试协调器、数据库、网络和应用宕机场景。

跨分片 Join、聚合与分页

Join

优先支持相同分片键、相同路由结果的绑定表 Join。任意跨库大表 Join 成本高,部分 SQL 形态还可能受支持范围限制。常用替代方式:

  • 冗余必要字段。
  • 广播小维表。
  • 先批量查询一侧 ID,再按分片批量查询。
  • 构建搜索或查询宽表。
  • 同步到数仓或 OLAP。

聚合

例如全局 sum(amount),各分片先算局部结果,中间件再归并。avg 不能简单平均各分片平均值,需要用总和与总数重新计算。聚合列、分组键和排序组合越复杂,归并成本越高。

深分页

sql
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 修改拓扑。

从单表迁移到分片表

一个安全迁移过程通常包括:

mermaid
flowchart TD
    A["确定新规则并压测"] --> B["创建真实库表"]
    B --> C["迁移历史全量数据"]
    C --> D["同步增量变更"]
    D --> E["按主键/行数/校验和核对"]
    E --> F["灰度切换读取"]
    F --> G["切换写入并观察"]
    G --> H["保留回滚窗口"]

核心问题:

  1. 迁移期间新写入如何不漏。
  2. 更新和删除如何按顺序同步。
  3. 双写一边成功一边失败如何补偿。
  4. 校验不只看总行数,还要按分片范围和业务字段核对。
  5. 切换期间旧应用是否仍可能写旧表。
  6. 回滚后增量如何反向补回。

ShardingSphere 提供的迁移或扩缩容能力会随版本演进,但工具不能替代业务停写窗口、数据校验、灰度与回滚设计。

扩容为什么不能直接改取模数

原规则:

text
ds = user_id % 2

直接改成:

text
ds = user_id % 4

大量已有用户的计算结果会变化,但历史数据还在旧分片,造成查不到或重复。扩容需要新旧路由并存、数据搬迁、增量同步、校验和切换。

减少数据搬迁可以考虑一致性哈希、虚拟节点、范围映射或路由表,但每种方案都会增加路由状态和运维复杂度。不要为了未来可能扩容就过早实现复杂算法。

生产监控

至少覆盖:

层次指标
应用或 ProxyQPS、P95/P99、错误率、线程、堆内存、GC
路由单分片/多分片/全路由比例、实际 SQL 数量、路由失败
连接池每个数据源活跃、空闲、等待、超时连接数
MySQLQPS、锁、慢 SQL、Buffer Pool、CPU、IO、磁盘
复制延迟、复制线程状态、错误和位点
数据质量分片行数、业务校验、双写失败、补偿积压

如果只能看到逻辑 SQL,看不到真实路由节点和改写后的 SQL,分片问题很难排查。SQL 日志应采样、脱敏并控制量。

故障排查流程

查不到数据

  1. 确认逻辑 SQL 的分片值、类型和是否为 NULL。
  2. 用相同算法手工计算目标库表。
  3. 查看路由日志和真实 SQL。
  4. 直接查询目标真实表确认数据位置。
  5. 检查写入时和读取时是否使用了不同规则版本。
  6. 检查迁移、双写和 Hint 上下文。

同一主键出现重复数据

  1. 检查主键生成器 worker 是否冲突。
  2. 检查业务唯一键是否只在单个真实表内生效。
  3. 检查重试是否幂等。
  4. 检查迁移双写与补偿是否重复执行。
  5. 检查同一分片值是否被不同算法路由。

查询突然变慢

  1. 比较是否从单分片退化为多分片或全路由。
  2. 查看真实 SQL 数量、各分片耗时和归并耗时。
  3. 检查新增的排序、聚合、深分页和大结果集。
  4. 检查各数据源连接池和单个热点分片。
  5. 对真实表 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 和异常重试;上线后监控全路由比例、连接池、各分片延迟和数据校验。

项目回答模板

text
我们先根据订单系统的访问模型选择分片键,高频的用户订单列表和订单写入都带 user_id,所以按 user_id 分库,让订单和明细作为绑定表使用同一分片规则,核心创建事务保持单分片 LOCAL 事务。订单 ID 使用分布式生成器,同时维护订单号到分片的路由能力,避免只按订单号查询时广播。全局搜索和报表不直接扫所有分片,而是通过 CDC 构建查询模型。接入层根据技术栈选择 ShardingSphere-JDBC 或 Proxy,并对核心 SQL 断言真实路由。上线监控全路由比例、各分片连接池、热点、复制延迟和数据校验。扩容时使用新旧规则并存、全量加增量迁移、校验、灰度切读和回滚窗口,不直接修改取模数。

关联知识点

本章小结

ShardingSphere 的价值是把逻辑 SQL 转换为对真实库表的访问,并提供分片、读写分离、事务和治理能力。真正决定系统是否可用的仍是分片键、事务边界、查询模型、连接容量、数据迁移和可观测性。面试时不要只背配置,要沿着“解析、路由、改写、执行、归并”解释运行原理,再说明广播查询、跨分片事务、深分页、复制延迟和扩容的代价。