第一章:ShardingSphere 概述与核心概念
1.1 什么是 ShardingSphere
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| ShardingSphere | Apache 基金会旗下的开源分布式数据库中间件套件,提供数据分片、读写分离、数据加密、影子库等能力 | 需区分其三种形态:Sharding-JDBC(客户端)、Sharding-Proxy(代理层)、Sharding-Sidecar(K8s) |
| 定位 | 分布式数据库中间件,增强 JDBC/DataSource/Proxy 层能力 | 不是数据库,而是对现有数据库的增强,兼容 MySQL、PostgreSQL 等协议 |
| 核心目标 | 提供透明化、可插拔、可扩展的数据库增强能力 | 支持无侵入或低侵入接入,支持灵活配置与扩展 |
| 开源协议 | Apache 2.0 | 可用于商业项目,具备良好的社区支持 |
1.2 ShardingSphere 的三大组件:Sharding-JDBC、Sharding-Proxy、Sharding-Sidecar
| 组件名称 | 说明 | 特点 | 注意事项 |
|---|---|---|---|
| Sharding-JDBC | 轻量级 Java 框架,以 JAR 包形式嵌入应用,直接操作数据库 | 无需额外部署,性能高,适用于 Java 应用 | 仅支持 Java,应用需管理多个数据源,升级依赖需重启应用 |
| Sharding-Proxy | 数据库代理服务,独立部署,应用通过标准数据库协议连接 | 语言无关,支持多语言客户端,集中管理分片规则 | 需额外部署和维护,存在网络跳转,性能略低于 Sharding-JDBC |
| Sharding-Sidecar | 基于 Kubernetes 的数据库代理组件,以 Sidecar 模式运行 | 与云原生架构集成,服务网格模式下自动注入 | 目前社区活跃度较低,生产使用较少,主要用于未来云原生场景探索 |
1.3 分库分表的基本概念:数据分片、分片键、分片策略、分片算法
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| 数据分片 | 将单一数据库或表的数据按规则分散到多个数据库或表中 | 目的是解决单库单表性能瓶颈和存储容量限制 |
| 分片键 | 决定数据分布的字段(如 user_id、order_id) | 应选择高基数、查询频繁的字段,避免热点数据 |
| 分片策略 | 定义如何根据分片键进行数据路由的规则,包含分库策略和分表策略 | 支持精确匹配、范围、复合条件等场景 |
| 分片算法 | 实现分片策略的具体逻辑,可自定义或使用内置算法 | 算法需保证一致性、可扩展性,避免扩容时大规模数据迁移 |
| 逻辑表 | 应用中使用的表名,如 t_order,实际对应多个真实表 | SQL 编写仍使用逻辑表名,由 ShardingSphere 解析并路由 |
| 真实表 | 物理数据库中的实际表,如 t_order_0, t_order_1 | 由分片算法决定插入或查询哪个真实表 |
| 数据节点 | 数据源 + 逻辑表 + 真实表的组合,如 ds0.t_order_0 | 每个数据节点对应一个物理存储位置 |
1.4 读写分离与主从架构支持
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| 读写分离 | 将写操作路由到主库,读操作路由到从库,提升读性能 | 适用于读多写少场景,需保证主从同步延迟可控 |
| 主从架构 | 一个主库(Master)负责写入,多个从库(Slave)通过 binlog 同步数据 | 需数据库层面支持主从复制机制 |
| 负载均衡策略 | 支持轮询、随机等方式在多个从库间分配读请求 | 可根据从库负载动态调整,避免单个从库压力过大 |
| 强制主库读 | 通过 Hint 或配置,强制某些读操作走主库 | 用于强一致性要求的查询,避免主从延迟导致数据不一致 |
| 中间件角色 | ShardingSphere 自动识别 SQL 类型并路由 | 不需应用层判断 SQL 类型,实现透明读写分离 |
1.5 数据加密、影子库、分布式治理等高级特性概览
| 特性名称 | 说明 | 注意事项 |
|---|---|---|
| 数据加密 | 对敏感字段(如身份证、手机号)进行透明加解密 | 支持 AES、MD5 等算法,逻辑列明文,密文列存储加密数据 |
| 影子库 | 为压测流量提供隔离环境,不影响生产数据 | 通过 SQL Hint 或标签识别影子 SQL,路由到影子库执行 |
| 分布式治理 | 支持配置中心(ZooKeeper、Nacos)和注册中心管理分片规则 | 实现配置动态刷新、服务发现、熔断降级等 |
| SQL 审计 | 记录 SQL 执行情况,用于安全审计和性能分析 | 可结合日志系统实现审计追踪 |
| 链路追踪 | 集成 SkyWalking、Zipkin 等,追踪 SQL 执行链路 | 便于定位慢查询和性能瓶颈 |
| 分布式事务 | 支持与 Seata 等框架集成,实现跨库事务一致性 | 建议在必要场景使用,避免滥用影响性能 |
第二章:Sharding-JDBC 入门实践
2.1 引入 Sharding-JDBC 依赖(Maven 配置)
| 依赖名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| sharding-jdbc-core | <dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-core</artifactId><version>4.1.1</version></dependency> | 核心模块,提供分片、读写分离等功能 | 用于 Java API 或 Spring 非 Boot 项目 | 版本号需根据项目需求选择,推荐 4.x 或 5.x 稳定版 |
| sharding-jdbc-spring-boot-starter | <dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><version>4.1.1</version></dependency> | Spring Boot 自动装配支持 | 用于 Spring Boot 项目,简化配置 | 引入后可通过 application.yml 配置分片规则 |
| mysql-connector-java | <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency> | MySQL 驱动 | 必须引入,ShardingSphere 通过 JDBC 连接数据库 | 建议使用 8.x 版本,注意时区、SSL 等连接参数 |
| druid-spring-boot-starter | <dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency> | 数据库连接池 | 可选,用于监控 SQL 执行、连接池状态 | ShardingSphere 默认使用 HikariCP,也可替换为 Druid |
2.2 基于 Java API 的简单分片配置
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| new ShardingRuleConfiguration() | ShardingRuleConfiguration config = new ShardingRuleConfiguration(); | 创建分片规则配置对象 | 用于编程方式定义分片规则 | 是 Java API 配置的入口对象 |
| setDataSourceMap | config.setDataSourceMap(dataSourceMap); | 设置数据源映射 | Map<String, DataSource> dataSourceMap = new HashMap<>(); | 需提前创建多个 DataSource 实例 |
| getTableRuleConfigs().add() | config.getTableRuleConfigs().add(tableRuleConfig); | 添加逻辑表的分片规则 | 每个逻辑表对应一个 TableRuleConfiguration | 必须设置逻辑表名、真实数据节点、分库分表策略 |
| setTableShardingStrategyConfig | tableRuleConfig.setTableShardingStrategyConfig(strategyConfig); | 设置分表策略 | 使用 StandardShardingStrategyConfiguration 等 | 支持标准、复合、Hint 等策略 |
| setDatabaseShardingStrategyConfig | tableRuleConfig.setDatabaseShardingStrategyConfig(strategyConfig); | 设置分库策略 | 同上 | 可与分表策略独立配置 |
| ShardingDataSource | DataSource dataSource = new ShardingDataSource(config); | 创建 Sharding 数据源 | 应用使用该 DataSource 执行 SQL | 生成后可注入 Spring 容器或直接使用 |
2.3 基于 YAML 配置文件的分片规则定义
| 配置项名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| dataSources | dataSources: ds0: ..., ds1: ... | 定义多个数据源 | 每个数据源包含 url、username、password 等 | 数据源名称用于后续规则引用 |
| shardingRule | shardingRule: ... | 分片规则根配置 | 包含 tables、bindingTables、broadcastTables 等 | 必须配置逻辑表与真实节点映射 |
| tables.[逻辑表名].actualDataNodes | actualDataNodes: ds$->{0..1}.t_order_$->{0..1} | 定义逻辑表对应的真实数据节点 | 使用行表达式生成 ds0.t_order_0, ds0.t_order_1 等 | 行表达式需保证生成合法表名 |
| tables.[逻辑表名].tableStrategy | tableStrategy: inline: ... | 配置分表策略 | 支持 inline、standard、complex、hint 等 | inline 适用于简单表达式,standard 支持自定义类 |
| tables.[逻辑表名].databaseStrategy | databaseStrategy: inline: ... | 配置分库策略 | 同上 | 可与分表策略组合使用 |
| defaultDataSourceName | defaultDataSourceName: ds0 | 设置默认数据源 | 未匹配分片规则的 SQL 路由到默认数据源 | 建议设置,避免路由失败 |
2.4 使用 Spring Boot 集成 Sharding-JDBC
| 配置项名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| spring.shardingsphere.datasource.names | spring.shardingsphere.datasource.names=ds0,ds1 | 声明所有数据源名称 | 名称用于后续配置引用 | 必须先声明才能使用 |
| spring.shardingsphere.datasource.[ds].type | spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource | 配置数据源类型 | 支持 HikariCP、Druid 等 | 需确保依赖已引入 |
| spring.shardingsphere.sharding.tables.[table].actual-data-nodes | spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds$->{0..1}.t_order_$->{0..1} | 配置逻辑表真实节点 | 使用 SpEL 表达式 | 注意转义 $ 和 {} |
| spring.shardingsphere.sharding.tables.[table].table-strategy.inline | spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column=order_id | 配置行表达式分表策略 | 设置分片列和表达式 | 表达式如 t_order_$->{order_id % 2} |
| spring.shardingsphere.sharding.tables.[table].database-strategy.inline | spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column=user_id | 配置行表达式分库策略 | 同上 | 可与分表策略组合使用 |
| @SpringBootApplication + @MapperScan | @SpringBootApplication @MapperScan("com.example.mapper") | 启动类注解 | 整合 MyBatis 时需扫描 Mapper 接口 | ShardingSphere 自动代理 DataSource |
2.5 简单 CRUD 操作验证分片效果
| 操作类型 | SQL 示例 | 预期行为 | 验证方法 | 注意事项 |
|---|---|---|---|---|
| INSERT | INSERT INTO t_order (user_id, order_id, info) VALUES (1, 1001, 'test') | 根据 user_id 分库,order_id 分表,插入对应真实表 | 查看目标库表是否生成数据 | 分片键必须提供,否则使用默认数据源 |
| SELECT | SELECT * FROM t_order WHERE user_id = 1 AND order_id = 1001 | 精确路由到单个真实表 | 使用 EXPLAIN 或日志查看执行计划 | 包含分片键可路由优化,否则需遍历所有表 |
| UPDATE | UPDATE t_order SET info = 'new' WHERE user_id = 1 AND order_id = 1001 | 路由到对应真实表执行更新 | 验证目标表数据是否更新 | 不支持跨分片批量更新 |
| DELETE | DELETE FROM t_order WHERE user_id = 1 AND order_id = 1001 | 删除指定分片数据 | 验证目标表数据是否删除 | 条件不含分片键将广播到所有表 |
| SELECT(全表) | SELECT * FROM t_order | 全路由(广播)到所有真实表,结果归并 | 查看是否从所有分片查询并合并结果 | 性能较差,生产慎用 |
第三章:数据分片核心机制
3.1 分片键(Sharding Key)的选择与作用
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| 分片键 | 决定数据在分库分表中分布的字段,如 user_id、order_id 等 | 必须出现在 SQL 的 WHERE 条件中才能实现精确路由 |
| 作用 | 实现数据水平拆分,决定 SQL 路由到哪个数据库或表 | 是分片策略和算法的输入依据 |
| 选择原则 | 高基数、分布均匀、查询频繁、避免热点 | 避免使用时间字段作为唯一分片键,易产生热点 |
| 单分片键 | 仅使用一个字段作为分片依据 | 最常见场景,如按 user_id 分库 |
| 复合分片键 | 使用多个字段联合决定分片,如 (user_id, order_id) | 用于复杂路由场景,需使用 ComplexShardingAlgorithm |
| 逻辑表与分片键 | 每个逻辑表可独立定义分片键 | 不同表可使用不同分片键 |
| 路由影响 | 包含分片键 → 精确路由;不包含 → 全路由(广播) | 全路由性能差,应尽量避免 |
3.2 分片策略(Sharding Strategy)详解
| 分片策略类型 | 说明 | 对应算法接口 | 注意事项 |
|---|---|---|---|
| StandardShardingStrategy | 标准分片策略,支持单分片键的精确匹配和范围查询 | StandardShardingAlgorithm | 最常用,适用于 =、IN、BETWEEN 等操作 |
| ComplexShardingStrategy | 复合分片策略,支持多分片键联合判断 | ComplexShardingAlgorithm | 用于复杂条件组合,性能开销较大 |
| InlineShardingStrategy | 行表达式分片策略,通过 Groovy 表达式定义分片逻辑 | 无(内置) | 适用于简单取模、字符串拼接等场景,配置简洁 |
| HintShardingStrategy | 强制分片策略,通过 Hint 信息(如 ThreadLocal)指定分片值 | HintShardingAlgorithm | 绕过 SQL 解析,强制路由到指定库表,用于特殊场景 |
| NoneShardingStrategy | 不分片策略,所有数据路由到默认数据源 | 无 | 用于广播表或默认表 |
3.3 分片算法(Sharding Algorithm)类型与自定义实现
| 算法类型 | 说明 | 实现方式 | 注意事项 |
|---|---|---|---|
| 自定义算法 | 实现 ShardingAlgorithm 接口或其子接口 | 编写 Java 类实现 doSharding 方法 | 需注册到 Spring 容器或配置中 |
| 内置算法 | ShardingSphere 提供的默认实现,如行表达式、取模等 | 通过配置直接使用 | 无需编码,适合简单场景 |
| 可插拔架构 | 算法通过 SPI 机制加载,支持动态替换 | 实现接口 + META-INF/services 注册 | 便于扩展和测试 |
| 算法配置方式 | 支持在 YAML、Java API、Spring Boot 中配置 | 使用 type 指定算法类型,props 传参 | 自定义算法需指定全类名 |
| 算法隔离 | 不同逻辑表可使用不同算法 | 配置独立 | 避免相互影响 |
3.4 行表达式分片算法(Inline Sharding Algorithm)
| 方法/配置项名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 行表达式语法 | ds_$->{user_id % 2} 或 t_order_$->{order_id % 4} | 定义数据源或表名生成规则 | ds0.t_order_0, ds1.t_order_1 等 | 使用 Groovy 语法,支持算术运算、字符串拼接 |
| sharding-column | sharding-column: user_id | 指定分片列 | 必须与表达式中变量一致 | 仅支持单列 |
| algorithm-expression | algorithm-expression: ds_$->{user_id % 2} | 设置行表达式 | 用于分库或分表策略 | 表达式结果必须对应真实数据节点 |
| 支持操作符 | +, -, *, /, %, {}, [] | 构建复杂表达式 | t_order_$->{order_id % 4 / 2} | 避免除零、越界等异常 |
| 性能 | 高效,无需反射调用 | 适用于简单规则 | 不适合复杂逻辑 |
3.5 标准分片算法(Standard Sharding Algorithm)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| doSharding | Collection<String> doSharding(Collection<String> availableTargetNames, PreciseShardingValue<T> shardingValue) | 精确分片(=, IN) | 根据 value 返回目标表名 | 必须返回非空集合,否则路由失败 |
| doSharding (Range) | Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<T> shardingValue) | 范围分片(BETWEEN) | 遍历 availableTargetNames 判断是否匹配 | 性能较差,建议避免大范围查询 |
| PreciseShardingValue | shardingValue.getValue() | 获取精确值(如 = 1001) | Long userId = shardingValue.getValue(); | 用于精确匹配 |
| RangeShardingValue | shardingValue.getValueRange().hasLowerBound() | 获取范围值(如 BETWEEN 1 AND 10) | 判断上下界并匹配 | 需遍历所有节点 |
| 自定义类实现 | public class MyStandardAlgorithm implements StandardShardingAlgorithm<Long> | 实现标准分片逻辑 | 需实现两个 doSharding 方法 | 必须有无参构造函数 |
3.6 复合分片算法(Complex Sharding Algorithm)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| doSharding | Collection<String> doSharding(Collection<String> availableTargetNames, Collection<ShardingValue> shardingValues) | 处理多个分片键 | 遍历 shardingValues 获取 user_id 和 order_id | shardingValues 包含所有分片键条件 |
| ShardingValue | if (value instanceof ListShardingValue) { ... } | 判断值类型(List、Range) | 支持 IN、BETWEEN 等多种条件 | 需处理不同类型 |
| 多键联合判断 | if (userId % 2 == 0 && orderId % 4 < 2) return Arrays.asList("t_order_0"); | 实现复杂路由逻辑 | 可结合业务规则 | 性能开销大,慎用 |
| 配置方式 | type: COMPLEX, props: algorithm-class-name: com.example.MyComplexAlg | YAML 或 Spring Boot 配置 | 必须指定 algorithm-class-name | 不支持行表达式 |
3.7 Hint 强制分片算法
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| HintManager.getInstance() | HintManager hintManager = HintManager.getInstance(); | 获取 Hint 管理器实例 | 必须在线程内使用 | 基于 ThreadLocal 实现 |
| addDatabaseShardingValue | hintManager.addDatabaseShardingValue("t_order", "user_id", 1); | 强制指定分库值 | 路由到 user_id=1 对应的库 | 优先级高于 SQL 解析 |
| addTableShardingValue | hintManager.addTableShardingValue("t_order", "order_id", 1001); | 强制指定分表值 | 路由到 order_id=1001 对应的表 | 可单独使用 |
| setMasterRouteOnly | hintManager.setMasterRouteOnly(); | 强制读操作走主库 | 用于强一致性查询 | 与读写分离配合使用 |
| close | hintManager.close(); | 释放资源,必须调用 | 建议使用 try-with-resources | 否则可能导致内存泄漏 |
第四章:分片策略与算法配置方式
4.1 基于 Spring Boot + YAML 的分片配置详解
| 配置项名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| spring.shardingsphere.sharding.tables.t_order.actual-data-nodes | ds$->{0..1}.t_order_$->{0..1} | 定义真实数据节点 | 使用行表达式生成 4 个节点 | 节点必须真实存在 |
| spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column | order_id | 设置分表列 | 必须与表达式匹配 | — |
| spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression | t_order_$->{order_id % 2} | 设置分表表达式 | 支持 Groovy 语法 | 注意转义 $ |
| spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column | user_id | 设置分库列 | 同上 | — |
| spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression | ds$->{user_id % 2} | 设置分库表达式 | 同上 | — |
| spring.shardingsphere.sharding.default-data-source-name | ds0 | 设置默认数据源 | 未匹配规则的 SQL 路由至此 | 建议设置 |
4.2 基于 Java API 的分片规则编程式配置
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| new TableRuleConfiguration | TableRuleConfiguration orderRule = new TableRuleConfiguration("t_order", "ds$->{0..1}.t_order_$->{0..1}"); | 创建逻辑表规则 | 设置逻辑表名和真实节点 | 节点表达式需正确 |
| new InlineShardingStrategyConfiguration | new InlineShardingStrategyConfiguration("user_id", "ds$->{user_id % 2}") | 创建行表达式分库策略 | 用于 databaseStrategy | 第一个参数为分片列,第二个为表达式 |
| new StandardShardingStrategyConfiguration | new StandardShardingStrategyConfiguration("order_id", "com.example.OrderIdShardingAlgorithm") | 创建标准分片策略 | 需提前定义算法类 | 用于复杂逻辑 |
| new ShardingRuleConfiguration | ShardingRuleConfiguration config = new ShardingRuleConfiguration(); | 创建分片规则总配置 | 添加所有 tableRule 和策略 | 最终用于构建 DataSource |
| new ShardingDataSource | DataSource dataSource = new ShardingDataSource(config); | 构建 Sharding 数据源 | 应用使用此数据源执行 SQL | 可注入 Spring 容器 |
4.3 使用 Spring 命名空间配置(XML 方式,了解即可)
| 配置元素名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| sharding:data-source | <sharding:data-source id="shardingDataSource" ... /> | 定义 Sharding 数据源 | 需引入 shardingsphere-namespace 命名空间 | 仅适用于传统 Spring 项目 |
| sharding:inline-strategy | <sharding:inline-strategy sharding-column="user_id" algorithm-expression="ds$->{user_id % 2}" /> | 配置行表达式策略 | 用于 database-strategy 或 table-strategy | 已逐渐被 YAML 和注解取代 |
| sharding:standard-strategy | <sharding:standard-strategy sharding-column="order_id" precise-algorithm-class-name="com.example.PreciseAlg" /> | 配置标准分片策略 | 需实现 PreciseShardingAlgorithm 接口 | 支持精确和范围算法 |
| sharding:key-generator | <sharding:key-generator type="SNOWFLAKE" column="order_id" /> | 配置分布式主键生成器 | 用于生成全局唯一 ID | — |
4.4 分片配置参数说明与最佳实践
| 配置参数名称 | 说明 | 最佳实践 |
|---|---|---|
| actual-data-nodes | 真实数据节点表达式 | 使用行表达式生成,确保覆盖所有物理表 |
| sharding-column | 分片列名称 | 选择高基数、查询频繁的字段 |
| algorithm-expression | 行表达式内容 | 避免复杂计算,保证性能 |
| strategy 配置 | 分库分表策略 | 尽量使用标准或行表达式策略,避免全路由 |
| default-data-source-name | 默认数据源 | 必须设置,防止路由失败 |
| binding-tables | 绑定表配置 | 将关联表(如 t_order 和 t_order_item)配置为绑定表,避免笛卡尔积 |
| broadcast-tables | 广播表配置 | 将小表(如字典表)配置为广播表,写操作同步到所有库 |
| sql.show | 是否打印 SQL | 开发环境开启,生产环境关闭 |
第五章:读写分离机制
5.1 读写分离原理与应用场景
| 概念 | 说明 | 注意事项 |
|---|---|---|
| 原理 | 将数据库的写操作(INSERT, UPDATE, DELETE)路由到主库(Master),读操作(SELECT)路由到从库(Slave),通过主从复制同步数据 | 基于 MySQL 的 binlog 复制机制实现数据一致性 |
| 核心组件 | MasterDataSource(主库)、SlaveDataSource(从库)、LoadBalanceStrategy(负载均衡策略) | ShardingSphere 通过解析 SQL 判断读写类型 |
| 应用场景 | 高并发读多写少的业务场景,如电商商品查询、社交平台动态展示等 | 不适用于强一致性要求极高的场景 |
| 优势 | 提升读性能、减轻主库压力、提高系统吞吐量 | 需注意主从延迟(Replication Delay)问题 |
| 限制 | 事务中的读操作默认走主库,保证一致性 | 非事务 SELECT 才可能走从库 |
5.2 配置主从数据源
| 配置项 | YAML 示例 | 说明 |
|---|---|---|
| spring.shardingsphere.datasource.names | names: master,slave0,slave1 | 定义所有数据源名称 |
| spring.shardingsphere.datasource.master.type | type: com.zaxxer.hikari.HikariDataSource | 主库数据源类型 |
| spring.shardingsphere.datasource.master.driver-class-name | driver-class-name: com.mysql.cj.jdbc.Driver | 数据库驱动类 |
| spring.shardingsphere.datasource.master.jdbc-url | jdbc-url: jdbc:mysql://localhost:3306/ds_master | 主库连接地址 |
| spring.shardingsphere.datasource.slave0.jdbc-url | jdbc-url: jdbc:mysql://localhost:3306/ds_slave0 | 从库连接地址 |
| spring.shardingsphere.rules.readwrite-splitting.data-sources.<ds_name> | readwrite-splitting: data-sources: ms-ds: write-data-source-name: master read-data-source-names: - slave0 - slave1 | 定义主从逻辑关系,ms-ds 为逻辑数据源名 |
5.3 读写分离负载均衡策略
| 策略类型 | 配置值 | 说明 | 使用场景 |
|---|---|---|---|
| 轮询策略 | ROUND_ROBIN | 请求按顺序轮流分配到各个从库 | 请求分布均匀,适合从库性能相近 |
| 随机策略 | RANDOM | 随机选择一个从库 | 简单高效,但可能分布不均 |
| 权重策略 | 自定义实现 | 按预设权重分配流量 | 从库配置不同时使用(如 2:1) |
| 配置方式 | load-balancer-name: ROUND_ROBIN | 在 readwrite-splitting 中配置 | 可通过 SPI 扩展自定义策略 |
✅ 最佳实践:生产环境推荐使用
ROUND_ROBIN,简单稳定。
5.4 强制走主库(Hint)机制
| 方法 | 代码示例 | 说明 |
|---|---|---|
| HintManager 设置 | try (HintManager hintManager = HintManager.getInstance()) { hintManager.setWriteDataSourceOnly(); // 此后的查询都走主库 orderMapper.selectById(1001);} | 使用 try-with-resources 确保资源释放 |
| setWriteDataSourceOnly() | hintManager.setWriteDataSourceOnly(); | 强制所有读操作也走主库 |
| 适用场景 | 强一致性查询 / 主从延迟敏感业务 / 事务后立即查询 | 避免因主从延迟导致的数据不一致 |
| 注意事项 | 必须在同一线程中调用,基于 ThreadLocal 实现 | 不支持跨线程传递 |
5.5 与分库分表的整合使用
| 整合方式 | 说明 | 配置要点 |
|---|---|---|
| 分库 + 读写分离 | 每个分片库都配置主从结构 | 每个 ds_${0..n} 都是一个主从逻辑组 |
| 分表 + 读写分离 | 分表不影响读写路由 | 读写分离在数据源层,分表在表层 |
| 完整架构 | 分库分表 + 读写分离 + 负载均衡 | 构建高可用高性能分布式数据库架构 |
YAML 配置结构示例:
rules:
sharding:
tables: ...
readwrite-splitting:
data-sources:
ds_0:
write-data-source-name: ds_0_master
read-data-source-names:
- ds_0_slave0
- ds_0_slave1
ds_1: ...
注意事项:
- 主从配置在
readwrite-splitting- 分片规则在
sharding- 逻辑表引用的是主从逻辑数据源
- 避免配置冲突
第六章:分布式主键与 ID 生成
6.1 分布式环境下主键冲突问题
| 问题 | 说明 | 解决方案 |
|---|---|---|
| 自增主键冲突 | 多库多表环境下,各库自增 ID 可能重复 | 改用分布式唯一 ID 生成器 |
| 数据合并困难 | 分片数据迁移或合并时 ID 冲突 | 使用全局唯一 ID |
| 水平扩展限制 | 自增主键无法跨库保证唯一性 | 分布式 ID 支持无限扩展 |
| 典型场景 | 插入 t_order 表,order_id 在 ds0.t_order_0 和 ds1.t_order_1 中不能重复 | 使用 Snowflake 或 UUID |
6.2 内置分布式主键生成策略
| 策略 | 类型值 | 说明 | 优点 | 缺点 |
|---|---|---|---|---|
| UUID | UUID | 生成 32 位字符串(含 -) | 全局唯一、性能高 | 长度长、无序、影响索引性能 |
| SNOWFLAKE | SNOWFLAKE | 64 位 Long 型 ID,含时间戳+机器码+序列号 | 有序、递增、适合索引 | 依赖系统时钟,时钟回拨可能导致问题 |
| LEAF | LEAF_SEGMENT | 美团开源,基于数据库号段预加载 | 高并发、低延迟 | 需额外部署 LEAF 服务 |
| DEFAULT | DEFAULT | 使用数据库自增(仅单库单表) | 简单 | 不适用于分片环境 |
✅ 推荐使用:
SNOWFLAKE适用于大多数场景。
6.3 自定义主键生成器
| 步骤 | 说明 | 示例代码 |
|---|---|---|
| 实现接口 | 实现 org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator 接口 | public class CustomKeyGenerator implements ShardingKeyGenerator |
| 重写方法 | generateKey() 方法返回 Comparable 类型 | @Overridepublic Comparable<?> generateKey() { return System.currentTimeMillis() + Thread.currentThread().getId();} |
| SPI 注册 | 在 META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator 文件中添加实现类全名 | com.example.CustomKeyGenerator |
| 配置使用 | 在 YAML 或 Java API 中引用 | type: CUSTOM |
6.4 主键生成策略的配置方式
YAML 配置方式:
spring:
shardingsphere:
rules:
sharding:
tables:
t_order:
actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: t-order-inline
key-generate-strategy:
column: order_id
key-generator-name: snowflake
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
uuid:
type: UUID
Java API 配置方式:
// 主键生成配置
KeyGenerateStrategyConfiguration keyStrategy =
new KeyGenerateStrategyConfiguration("order_id", "SNOWFLAKE");
// 主键生成器配置
Properties props = new Properties();
props.setProperty("worker-id", "123");
ShardingKeyGeneratorConfiguration keyGenConfig =
new ShardingKeyGeneratorConfiguration("SNOWFLAKE", props);
// 构建表规则
TableRuleConfiguration orderRule =
new TableRuleConfiguration("t_order", "ds$->{0..1}.t_order_$->{0..1}");
orderRule.setKeyGenerateStrategyConfiguration(keyStrategy);
orderRule.setShardingKeyGeneratorConfig(keyGenConfig);
✅ 最佳实践:
- 使用 SNOWFLAKE 并设置
worker-id避免集群冲突- 主键列必须配置
key-generate-strategy- 生产环境避免使用 UUID 作为索引列
第七章:数据加密机制
7.1 数据加密的必要性与场景
| 项目 | 说明 | 注意事项 |
|---|---|---|
| 必要性 | 防止敏感数据(如身份证、手机号、银行卡号)在数据库中明文存储,满足合规要求(如 GDPR、等保) | 即使数据库被拖库,也能保护核心数据 |
| 加密层级 | 在 ShardingSphere 中属于逻辑层加密,应用无需感知 | 数据库中存储的是密文,应用使用明文 |
| 典型场景 | 用户个人信息加密、支付信息保护、日志脱敏、合规审计要求 | 不建议对所有字段加密,影响性能和索引 |
| 透明性 | 对应用透明,SQL 仍使用逻辑列名操作 | 开发者无需修改业务代码 |
| 局限性 | 加密列无法使用范围查询(如 >、<)、不支持 LIKE 模糊查询(除非使用固定偏移) | 推荐对等值查询字段加密 |
7.2 逻辑列与密文列的概念
| 概念 | 说明 | 示例 |
|---|---|---|
| 逻辑列(Logic Column) | 应用层使用的列名,SQL 中直接引用 | SELECT user_name, phone FROM t_user WHERE phone = '13800138000' 中的 phone |
| 密文列(Cipher Column) | 数据库中实际存储的加密字段 | t_user 表中 phone_cipher 存储加密后的手机号 |
| 辅助列(Assisted Query Column) | 可选,用于支持模糊查询或哈希查询 | phone_hash 存储手机号的 MD5 值,用于等值匹配 |
| 关系 | 一个逻辑列对应一个或多个物理列(cipher + assisted) | 逻辑列 phone → 物理列 phone_cipher, phone_hash |
| 配置要求 | 必须明确指定逻辑列、密文列、加密算法 | ShardingSphere 自动完成加解密转换 |
7.3 加密算法配置(AES、MD5 等)
| 算法类型 | 配置 type 值 | 说明 | 是否可逆 | 适用场景 |
|---|---|---|---|---|
| AES | AES | 对称加密,使用密钥加解密 | ✅ 可逆 | 手机号、邮箱、地址等需解密使用的字段 |
| MD5 | MD5 | 哈希算法,不可逆 | ❌ 不可逆 | 仅用于等值比对,如密码校验(但推荐使用更安全的 BCrypt) |
| CHAR | CHAR | 明文存储(仅脱敏展示) | ✅ | 调试或脱敏场景 |
| CHOOSE | CHOOSE | 根据配置选择 AES 或 MD5 | — | 灵活配置 |
| 自定义算法 | 自定义类名 | 实现 EncryptAlgorithm 接口 | 按实现 | 满足特殊加密需求 |
✅ 最佳实践:
- 敏感信息使用 AES
- 密码建议使用 BCrypt(需自定义实现)
- 避免使用 MD5 存储密码(安全性低)
7.4 加解密配置与使用流程
| 步骤 | 说明 | 配置示例(YAML) |
|---|---|---|
| 1. 定义加密规则 | 在 spring.shardingsphere.rules.encrypt 下配置 | encrypt: encryptors: aes_encryptor: type: AES props: aes-key-value: 123456abcde |
| 2. 配置加密表与列 | 指定哪些表的哪些列需要加密 | tables: t_user: columns: phone: cipher-column: phone_cipher encryptor-name: aes_encryptor |
| 3. 应用使用逻辑列 | SQL 中使用 phone 而非 phone_cipher | INSERT INTO t_user(phone) VALUES('13800138000') |
| 4. 自动加解密 | ShardingSphere 拦截 SQL,自动加密写入,解密读取 | 无需代码改动 |
| 5. 数据库结构 | 物理表需包含 phone_cipher 字段 | 类型通常为 VARCHAR(255) |
7.5 查询与插入时的透明加解密
| 操作 | 流程 | 示例 |
|---|---|---|
| INSERT | 1. SQL 使用逻辑列 phone 2. ShardingSphere 获取明文 3. 使用 AES 加密为密文 4. 写入 phone_cipher 字段 | INSERT INTO t_user(phone) VALUES('13800138000') → 实际写入 phone_cipher = ‘abc123xyz’ |
| SELECT | 1. SQL 查询 phone 2. 从 phone_cipher 读取密文 3. 自动解密为明文 4. 返回给应用 | SELECT phone FROM t_user → 返回 ‘13800138000’ |
| WHERE 查询 | 条件中的明文被加密后用于查询密文列 | WHERE phone = '13800138000' → WHERE phone_cipher = '加密后的值' |
| 辅助查询列 | 若配置 assisted-query-column,则使用哈希值进行等值匹配 | WHERE phone_hash = MD5('13800138000') |
| 限制 | 不支持 ORDER BY phone(密文无序)、不支持 GROUP BY phone | 建议对加密列仅做等值查询 |
第八章:影子库(Shadow Database)用于压测
8.1 影子库的概念与作用
| 概念 | 说明 | 价值 |
|---|---|---|
| 影子库(Shadow Database) | 与生产库结构相同但用于压测的独立数据库 | 避免压测流量影响真实用户 |
| 影子表(Shadow Table) | 被压测的特定逻辑表(如 t_order) | 精准控制压测范围 |
| 作用 | 在不影响生产环境的前提下,使用真实流量对新架构、SQL 优化、版本升级进行压测验证 | 提升系统稳定性,降低上线风险 |
| 核心思想 | ”影子”流量与”真实”流量并行,但写入不同库 | 流量复制,数据隔离 |
8.2 影子库与生产库的隔离机制
| 隔离维度 | 说明 | 实现方式 |
|---|---|---|
| 数据源隔离 | 影子库使用独立的数据源,与生产库完全物理隔离 | 配置不同的 JDBC URL、用户名、密码 |
| 网络隔离 | 影子库通常部署在独立网络或测试环境 | 防止资源争抢 |
| 资源隔离 | 影子库可使用较低配置,不影响生产性能 | 成本可控 |
| 数据清理 | 影子库数据可随时清空,不影响生产 | 支持重复压测 |
| 权限控制 | 运维人员可控制影子库访问权限 | 安全可控 |
8.3 影子表与影子规则配置
| 配置项 | YAML 示例 | 说明 |
|---|---|---|
| spring.shardingsphere.rules.shadow.data-sources.<shadow_name> | shadow: data-sources: shadow_ds: source-data-source-name: ds_0 shadow-data-source-name: shadow_ds_0 | 定义影子数据源映射关系 |
| spring.shardingsphere.rules.shadow.tables.t_order | tables: t_order: data-source-names: - shadow_ds | 指定 t_order 表启用影子规则 |
| spring.shardingsphere.rules.shadow.column | column: sharding | 指定用于判断影子流量的列(如 sharding) |
| spring.shardingsphere.rules.shadow.operation | operation: INSERT,UPDATE | 指定哪些操作触发影子路由 |
8.4 基于 SQL Hint 的影子路由
| 方法 | 代码示例 | 说明 |
|---|---|---|
| HintManager 启用影子 | try (HintManager hintManager = HintManager.getInstance()) { hintManager.setShadow(true); orderService.createOrder(order);} | 在代码中强制开启影子路由 |
| 自动识别(列匹配) | 当 SQL 中包含 sharding = 'shadow' 时自动路由 | 无需代码改动,通过 SQL 条件触发 |
| HTTP Header 传递 | 结合网关,在 Header 中传递影子标记 | 全链路压测常用方式 |
| 优先级 | Hint > 列匹配规则 | Hint 强制生效 |
8.5 与真实流量并行的压测方案
| 步骤 | 说明 | 技术要点 |
|---|---|---|
| 1. 流量复制 | 将生产流量复制一份到影子环境 | 使用日志、消息队列或代理工具 |
| 2. 标记影子流量 | 在 SQL 或请求中添加影子标记 | 如设置 sharding=‘shadow’ 或使用 Hint |
| 3. 并行执行 | 同一条 SQL 同时在生产库和影子库执行 | 生产库处理真实请求,影子库记录性能 |
| 4. 监控对比 | 对比 SQL 执行时间、慢查询、TPS 等指标 | 验证优化效果 |
| 5. 安全保障 | 影子库不返回结果给用户,仅用于监控 | 防止数据泄露 |
| 优势 | 真实场景压测、零业务侵入、可重复验证 | 适用于 SQL 优化、索引调整、版本升级 |
✅ 最佳实践:
- 影子库与生产库网络隔离
- 压测期间监控数据库负载
- 使用独立线程池执行影子 SQL,避免阻塞主流程
第九章:Sharding-Proxy 入门与使用
9.1 Sharding-Proxy 架构与工作原理
| 概念 | 说明 | 特点 |
|---|---|---|
| 定位 | 分布式数据库中间件,以代理形式提供数据库服务 | 类似 MySQL Proxy,但功能更强大 |
| 架构模型 | 客户端 → Sharding-Proxy → 真实数据库(MySQL/PostgreSQL) | 应用无感知,透明接入 |
| 协议兼容 | 支持 MySQL / PostgreSQL 二进制协议 | 可使用标准客户端工具连接 |
| 核心功能 | SQL 解析与路由、分片执行、结果归并、读写分离、数据加密、影子库 | 功能与 ShardingSphere-JDBC 一致 |
| 部署方式 | 独立进程部署,可集群化 + 负载均衡 | 适合异构语言环境 |
| 对比 JDBC 模式 | Proxy 模式对应用完全透明,JDBC 模式需引入依赖 | Proxy 更适合多语言、遗留系统 |
9.2 安装与启动 Sharding-Proxy
| 步骤 | 操作 | 命令/说明 |
|---|---|---|
| 1. 下载 | 从 Apache ShardingSphere 官网下载二进制包 | apache-shardingsphere-5.x.x-sharding-proxy-bin.tar.gz |
| 2. 解压 | 解压到指定目录 | tar -zxvf apache-shardingsphere-5.x.x-sharding-proxy-bin.tar.gz |
| 3. 目录结构 | — | sharding-proxy/├── bin/├── conf/├── lib/└── logs/ |
| 4. 启动 | 使用 start.sh 脚本启动 | bin/start.sh 3307(指定端口,默认 3307) |
| 5. 验证 | 查看日志 logs/stdout.log 是否启动成功 | 或使用 mysql -h127.0.0.1 -P3307 -uroot -p 连接 |
| 6. 停止 | bin/stop.sh | 发送 STOP 信号 |
✅ 注意:需提前安装 Java 8+ 环境。
9.3 配置 server.yaml 与 config-sharding.yaml
server.yaml(全局配置):
# 权限配置
authentication:
users:
root:
password: root
sharding:
password: sharding
authorizedSchemas: sharding_db
# 通用属性
props:
max-connections-size-per-query: 1
executor-size: 16 # 工作线程数
proxy-backend-query-fetch-size: 1000
proxy-front-end-executor-size: 16
sql-show: true # 是否打印 SQL
config-sharding.yaml(分片规则):
schemaName: sharding_db
dataSources:
ds_0:
url: jdbc:mysql://127.0.0.1:3306/ds_0?serverTimezone=UTC&useSSL=false
username: root
password: root
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
ds_1:
url: jdbc:mysql://127.0.0.1:3306/ds_1?serverTimezone=UTC&useSSL=false
username: root
password: root
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
rules:
- !SHARDING
tables:
t_order:
actualDataNodes: ds_${0..1}.t_order_${0..1}
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: t_order_inline
keyGenerateStrategy:
column: order_id
keyGeneratorName: snowflake
shardingAlgorithms:
t_order_inline:
type: INLINE
props:
algorithm-expression: t_order_${order_id % 2}
keyGenerators:
snowflake:
type: SNOWFLAKE
✅ 说明:
- schemaName 对应逻辑数据库名
- actualDataNodes 定义真实数据节点
- 支持 config-readwrite-splitting.yaml、config-encrypt.yaml 等独立配置
9.4 使用 MySQL/PostgreSQL 客户端连接 Proxy
| 客户端 | 连接命令 | 说明 |
|---|---|---|
| MySQL 命令行 | mysql -h127.0.0.1 -P3307 -uroot -p | 默认端口 3307,用户在 server.yaml 中定义 |
| Navicat / DBeaver | 新建 MySQL 连接,主机 127.0.0.1,端口 3307 | 可视化操作分片表 |
| JDBC 连接 | jdbc:mysql://127.0.0.1:3307/sharding_db | 应用可通过 Proxy 访问逻辑库 |
| 验证分片 | 执行 INSERT INTO t_order(order_id) VALUES(1), (2); | 观察数据是否分布到不同表 |
| 注意事项 | 确保 Proxy 正在运行、防火墙开放端口、数据库服务可访问 | 连接失败检查日志 |
9.5 Proxy 模式下的分片与读写分离验证
| 验证项 | 操作 | 预期结果 |
|---|---|---|
| 分片插入 | INSERT INTO t_order(order_id, user_id) VALUES(1001, 1), (1002, 2); | 数据应分布到 ds_0.t_order_1 和 ds_1.t_order_0 |
| 分片查询 | SELECT * FROM t_order WHERE order_id = 1001; | 返回对应分片数据,SQL 被路由到单一节点 |
| 广播查询 | SELECT * FROM t_order; | 查询所有分片并合并结果 |
| 读写分离 | 配置 readwrite-splitting 规则后,执行 SELECT | 读请求应走从库(可通过日志或 sql-show 验证) |
| Hint 强制主库 | 使用 /* sharding hint: write_ds */ 注释 | 读操作也走主库 |
| 性能监控 | 查看 logs/stdout.log 中的 SQL 日志 | 确认路由正确性与执行时间 |
✅ 建议:开启
sql-show: true便于调试。
第十章:Spring Boot 集成与配置详解
10.1 Spring Boot 自动装配机制
| 组件 | 自动配置类 | 说明 |
|---|---|---|
| ShardingSphereDataSource | ShardingSphereAutoConfiguration | 根据 application.yml 自动创建数据源 |
| 依赖引入 | shardingsphere-jdbc-core-spring-boot-starter | 启用自动装配 |
| 条件装配 | 基于 @ConditionalOnClass, @ConditionalOnProperty | 只有配置了规则才生效 |
| 配置前缀 | spring.shardingsphere | 所有配置项的根路径 |
| 启动流程 | 1. 读取 YAML 配置 2. 构建数据源 3. 注册为 Spring Bean 4. 替换默认 DataSource | 对 MyBatis/JPA 透明 |
10.2 application.yml 中的完整分片配置示例
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/ds0?serverTimezone=UTC&useSSL=false
username: root
password: root
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/ds1?serverTimezone=UTC&useSSL=false
username: root
password: root
rules:
# 分片规则
sharding:
tables:
t_order:
actual-data-nodes: ds${0..1}.t_order_${0..1}
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: t-order-inline
key-generate-strategy:
column: order_id
key-generator-name: snowflake
t_order_item:
actual-data-nodes: ds${0..1}.t_order_item_${0..1}
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: t-order-item-inline
sharding-algorithms:
t-order-inline:
type: INLINE
props:
algorithm-expression: t_order_${order_id % 2}
t-order-item-inline:
type: INLINE
props:
algorithm-expression: t_order_item_${order_id % 2}
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 1
# 读写分离
readwrite-splitting:
data-sources:
ms-ds:
write-data-source-name: ds0
read-data-source-names:
- ds0
- ds1
load-balancer-name: round-robin
# 加密(可选)
encrypt:
encryptors:
aes-encryptor:
type: AES
props:
aes-key-value: 123456abc
tables:
t_user:
columns:
phone:
cipher-column: phone_cipher
encryptor-name: aes-encryptor
props:
sql-show: true # 显示实际执行 SQL
10.3 多数据源配置与事务管理注意事项
| 项目 | 说明 | 建议 |
|---|---|---|
| 本地事务 | 单一分片操作支持本地事务 | @Transactional 有效 |
| 分布式事务 | 跨分片操作需引入 Seata 或 Atomikos | ShardingSphere 支持 XA 和 BASE 事务 |
| 事务限制 | 广播表更新不保证原子性、读写分离中事务内读走主库 | 避免跨库复杂事务 |
| 连接泄漏 | 确保 DataSource 配置合理连接池参数 | 使用 HikariCP 并设置超时 |
| Hint 与事务 | HintManager 必须在事务开始前设置 | 否则可能路由错误 |
10.4 与 MyBatis / JPA 的整合使用
MyBatis 整合:
<!-- Maven 依赖 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
@Mapper
public interface OrderMapper {
@Insert("INSERT INTO t_order (order_id, user_id) VALUES (#{orderId}, #{userId})")
void insert(Order order);
@Select("SELECT * FROM t_order WHERE order_id = #{orderId}")
Order selectById(Long orderId);
}
✅ 无需额外配置,ShardingSphere 自动拦截 MyBatis 的 SQL。
Spring Data JPA 整合:
@Entity
@Table(name = "t_order")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long orderId;
private Integer userId;
// getter/setter
}
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
}
⚠️ 注意:
- JPA 的
@GeneratedValue与 ShardingSphere 冲突,必须关闭(使用 key-generate-strategy)- 建议使用
GenerationType.NONE或自定义 ID 生成
10.5 配置项详解与常见错误排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Cannot resolve datasource | datasource.names 配置错误或数据源未定义 | 检查 names 与实际数据源名称一致 |
| SQL 路由错误 | 分片算法表达式错误 | 检查 algorithm-expression 语法 |
| 主键生成失败 | 未配置 key-generate-strategy | 确保分片列配置了主键生成器 |
| 读写分离不生效 | 未开启 readwrite-splitting 或负载均衡配置错误 | 检查规则名称与数据源映射 |
| 启动报错 ClassNotFoundException | 缺少数据库驱动 | 手动添加 mysql-connector-java 依赖 |
| 事务跨分片失败 | 未配置分布式事务 | 引入 Seata 或使用 @ShardingSphereTransactionType |
| sql-show 不打印 | props.sql-show 未开启 | 设置为 true 并检查日志级别 |
✅ 调试建议:
- 开启
sql-show: true- 使用 HintManager 强制路由测试
- 检查 actual-data-nodes 是否匹配真实表结构
第十一章:高阶特性与扩展机制
11.1 自定义分片算法 SPI 扩展
| 类型 | 接口 | 说明 | 实现步骤 |
|---|---|---|---|
| 标准分片 | StandardShardingAlgorithm | 支持单分片键的精确/范围查询 | 实现 doSharding() 方法 |
| 复合分片 | ComplexKeysShardingAlgorithm | 支持多分片键联合路由 | 处理 Collection |
| Hint 分片 | HintShardingAlgorithm | 基于 Hint 强制路由,忽略 SQL 中的分片条件 | 适用于分片键不在 SQL 中的场景 |
| 行表达式分片 | InlineShardingAlgorithm | 内置,支持 t_order_${id % 2} 表达式 | 无需自定义 |
SPI 扩展步骤:
- 实现对应接口:
public class CustomDBShardingAlgorithm implements StandardShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
return "ds_" + (shardingValue.getValue() % 2);
}
}
- 在
META-INF/services/org.apache.shardingsphere.sharding.spi.ShardingAlgorithm文件中添加类全名:
com.example.CustomDBShardingAlgorithm
- YAML 配置引用:
sharding-algorithms:
custom-db-algorithm:
type: CUSTOM
props:
strategy: STANDARD
algorithm-class-name: com.example.CustomDBShardingAlgorithm
✅ 适用场景:复杂业务逻辑分片(如按城市+时间组合分片)。
11.2 分布式事务支持(与 Seata 整合)
| 事务模式 | 说明 | 配置方式 |
|---|---|---|
| XA 模式 | 强一致性,基于两阶段提交(2PC) | 需数据库支持 XA,性能较低 |
| BASE 模式 | 最终一致性,基于 Seata 的 AT 模式 | 高性能,推荐生产使用 |
| 本地事务 | 单分片内事务,Spring @Transactional 可用 | 默认模式 |
整合 Seata 步骤:
- 添加依赖:
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.7.1</version>
</dependency>
- 配置 application.yml:
spring:
shardingsphere:
transaction:
type: BASE # 或 XA
props:
proxy-transaction-type: BASE
- 使用
@GlobalTransactional注解:
@GlobalTransactional
public void transfer(Order order, Item item) {
orderMapper.insert(order);
itemMapper.update(item);
}
✅ 注意:跨分片更新必须使用分布式事务,否则不保证一致性。
11.3 配置中心与注册中心集成(ZooKeeper, Nacos)
| 组件 | 作用 | 配置方式 |
|---|---|---|
| 配置中心 | 动态管理分片、读写分离等规则 | 支持 ZooKeeper、Nacos、Etcd |
| 注册中心 | 服务发现与高可用 | Proxy 集群注册 |
| 优势 | 配置热更新、集群协同、降级容错 | 无需重启应用 |
Nacos 配置示例:
spring:
shardingsphere:
mode:
type: Cluster
repository:
type: Nacos
props:
server-lists: 127.0.0.1:8848
namespace: shardingsphere
data-id: sharding-config
group: DEFAULT_GROUP
ZooKeeper 配置示例:
mode:
type: Cluster
repository:
type: ZooKeeper
props:
retryIntervalMilliseconds: 500
timeToLiveSeconds: 60
maxRetries: 3
operationTimeoutMilliseconds: 500
server-lists: localhost:2181
namespace: sharding-sphere
✅ 最佳实践:生产环境推荐使用 Nacos,界面友好,集成简单。
11.4 SQL 解析引擎与执行优化
| 组件 | 说明 | 优化点 |
|---|---|---|
| SQL Parser | 基于 ANTLR 解析 SQL,生成 AST | 支持 MySQL/PostgreSQL/Oracle/SQLServer |
| Routing Engine | 根据分片键路由到目标数据节点 | 减少广播查询 |
| Rewrite Engine | 改写 SQL 以适应分片结构 | 如 t_order → t_order_1 |
| Merge Engine | 归并来自多个数据节点的结果集 | 支持排序、分页、聚合 |
| 执行优化 | 并行执行、连接池优化、批处理 | 提升吞吐量 |
优化建议:
- 避免
SELECT *,明确字段提升解析效率 - 使用绑定表(Binding Table)减少归并压力
- 合理设置
max-connections-size-per-query
11.5 链路追踪与监控指标暴露
| 监控方式 | 说明 | 集成方案 |
|---|---|---|
| 链路追踪 | 跟踪 SQL 从入口到执行的完整路径 | 支持 SkyWalking、Zipkin、OpenTelemetry |
| Metrics 暴露 | 暴露 JVM、连接池、SQL 执行等指标 | 集成 Prometheus + Grafana |
| 日志增强 | 记录 SQL、路由、耗时等信息 | sql-show: true + 自定义 Appender |
Prometheus 配置:
props:
metrics:
type: PROMETHEUS
host: 127.0.0.1
port: 9199
node-path: /metrics
访问 http://127.0.0.1:9199/metrics 可获取指标:
# HELP sharding_sphere_execution_sql_count Total SQL execution count
# TYPE sharding_sphere_execution_sql_count counter
sharding_sphere_execution_sql_count{type="SELECT"} 123
✅ 运维价值:实现 APM 全景监控,快速定位性能瓶颈。
第十二章:性能调优与运维实践
12.1 分片策略对查询性能的影响
| 策略 | 查询性能 | 适用场景 | 建议 |
|---|---|---|---|
| 取模分片 | 均匀分布,但范围查询需扫描所有分片 | 写多读少,数据均匀 | — |
| 范围分片 | 范围查询快,但易产生热点 | 时间序列数据(如日志) | 配合分片键预估数据量 |
| 哈希分片 | 分布较均匀,支持等值查询 | 用户 ID、订单号等 | — |
| 复合分片 | 灵活,但路由复杂 | 多维度查询 | 避免过度设计 |
⚠️ 避免:order_id 递增 + 取模,可能导致热点(集中在最新分片)。
12.2 全局表、广播表的使用场景
| 表类型 | 说明 | 使用场景 | 注意事项 |
|---|---|---|---|
| 广播表 | 每个分片库都有一份完整副本 | 字典表、配置表、权限规则 | 写操作会广播到所有节点,影响性能 |
| 全局表 | 同步所有节点,通常用于维度表 | 与分片表关联查询(JOIN) | 需保证数据一致性 |
配置方式:
tables:
t_config:
actual-data-nodes: ds0.t_config,ds1.t_config
table-strategy:
none:
✅ 优势:避免跨库 JOIN,提升查询效率。
12.3 分页查询优化与归并排序
| 问题 | 说明 | 优化方案 |
|---|---|---|
| 深层分页 | LIMIT 1000000, 10 需从各分片拉取大量数据 | 改用 WHERE id > ? LIMIT 10(基于索引) |
| 归并排序 | 多分片结果需在内存归并 | 确保 ORDER BY 字段为分片键或全局唯一 |
| 内存占用 | 归并大量数据可能导致 OOM | 设置 max-merge-group-count 限制 |
优化建议:
- 分页尽量基于分片键(如 user_id)
- 避免
SELECT *+ORDER BYnon-sharding-column - 使用游标分页(Cursor-based Pagination)
12.4 慢 SQL 分析与执行计划查看
| 工具 | 方法 | 说明 |
|---|---|---|
| ShardingSphere 日志 | 开启 sql-show: true | 查看实际执行的 SQL 和路由信息 |
| 数据库 EXPLAIN | 在目标分片库执行 EXPLAIN | 分析单个分片的执行计划 |
| 慢查询日志 | 开启 MySQL 慢日志 | 定位耗时 SQL |
| APM 工具 | SkyWalking / Prometheus | 监控 SQL 执行时间分布 |
分析流程:
- 通过日志定位慢 SQL
- 确认路由到哪些分片
- 在对应分片库执行 EXPLAIN
- 优化索引或分片策略
12.5 运维命令与健康检查
| 功能 | 命令/方式 | 说明 |
|---|---|---|
| 健康检查 | /actuator/health(Spring Boot) | 返回 shardingsphere 状态 |
| 元数据查看 | SHOW TABLES; / DESC t_order;(通过 Proxy) | 查看逻辑表结构 |
| 运行时配置 | 通过配置中心动态更新规则 | 无需重启 |
| 连接池监控 | SHOW DATABASE CONNECTIONS;(Proxy) | 查看当前连接数 |
| 数据一致性检查 | 自定义脚本比对分片数据 | 定期校验 |
| 优雅下线 | 关闭写入 → 等待读完成 → 停止服务 | 避免数据丢失 |
✅ 运维建议:
- 建立监控告警体系
- 定期演练故障恢复
- 使用蓝绿部署或灰度发布更新分片规则
第十三章:常见问题与最佳实践
13.1 跨分片查询的限制与解决方案
| 限制类型 | 说明 | 解决方案 |
|---|---|---|
| JOIN 查询 | 不支持跨分片 JOIN(非绑定表) | ✅ 绑定表(Binding Table):相同分片键的表配置为绑定关系,可高效 JOIN ✅ 应用层归并:在代码中分步查询后合并 ✅ 冗余字段:将关联字段冗余到主表(如订单表冗余用户姓名) |
| 子查询 | 复杂子查询可能无法正确路由 | ✅ 拆分为多个简单查询 ✅ 避免在 WHERE 子句中使用非分片键的子查询 |
| 聚合函数 | SUM, COUNT, AVG 需从所有分片拉取数据 | ✅ 使用归并引擎自动处理 ⚠️ 注意内存占用,避免全表聚合 |
| 排序(ORDER BY) | 非分片键排序需在内存归并 | ✅ 尽量按分片键排序 ✅ 分页使用 WHERE id > ? LIMIT n 替代 LIMIT offset, n |
| 分页(Paging) | 深层分页性能差(如 LIMIT 1000000, 10) | ✅ 游标分页(Cursor-based Pagination):基于有序 ID 分页 ✅ 二次查询法:先查 ID 再查详情 |
| DISTINCT / GROUP BY | 非分片键去重或分组需归并 | ✅ 尽量在分片键上操作 ✅ 应用层处理复杂去重逻辑 |
✅ 最佳实践:
- 设计表结构时优先考虑分片键一致性
- 复杂查询建议在应用层或中间层处理
- 使用
sql-show: true验证实际执行路径
13.2 分布式事务的取舍与建议
| 事务模式 | 一致性 | 性能 | 适用场景 | 建议 |
|---|---|---|---|---|
| 本地事务 | 单分片内强一致 | 高 | 单一数据节点操作 | 默认使用 |
| XA(2PC) | 强一致 | 低(锁资源久) | 对一致性要求极高,可接受性能损失 | 不推荐生产大规模使用 |
| BASE(Seata AT) | 最终一致 | 高 | 大多数跨分片业务场景 | ✅ 生产推荐 |
| TCC / Saga | 最终一致 | 中 | 复杂业务流程,需自定义补偿 | 适合订单、支付等长流程 |
决策建议:
- 优先避免跨分片事务:通过合理分片设计让相关数据落在同一分片
- 能用本地事务就不用分布式事务
- 强一致场景:评估业务容忍度,必要时使用 XA
- 高并发场景:使用 BASE 模式 + 重试机制
- 幂等性保障:所有写操作必须实现幂等,防止重试导致数据错乱
⚠️ 注意:ShardingSphere 的分布式事务依赖外部协调器(如 Seata Server),需额外部署和维护。
13.3 分片键设计的最佳实践
| 原则 | 说明 | 示例 |
|---|---|---|
| 高基数(High Cardinality) | 分片键取值范围大,避免热点 | ✅ 用户 ID、订单 ID ❌ 性别、状态(低基数) |
| 均匀分布(Uniform Distribution) | 数据在各分片间分布均衡 | ✅ user_id % 4 ❌ 时间戳取模(易热点) |
| 频繁查询(Query Frequency) | 分片键应是 WHERE 条件中最常出现的字段 | ✅ 订单查询按 user_id 分片 ❌ 按 order_date 分片但常查用户订单 |
| 避免热点(Hotspot Avoidance) | 防止单一分片负载过高 | ✅ 使用 Snowflake ID + 取模 ❌ 自增 ID 导致写入集中在最新分片 |
| 不变性(Immutability) | 分片键一旦确定不应修改 | ✅ 用户 ID ❌ 用户所属区域(可能变更) |
| 组合分片键 | 多维度查询时使用复合分片 | ✅ (user_id, order_type) 联合分片 |
设计流程:
- 分析核心业务查询模式
- 识别高频查询字段
- 评估数据量和增长趋势
- 选择合适的分片算法(取模、哈希、范围等)
- 压测验证分布均匀性
✅ 推荐分片键:
- 用户中心:user_id
- 订单系统:order_id(Snowflake)或 user_id
- 日志系统:DATE + HOUR 范围分片
13.4 数据迁移与扩容方案
| 阶段 | 操作 | 工具/方法 |
|---|---|---|
| 1. 预评估 | 评估数据量、迁移时间、停机窗口 | mysqldump, pt-online-schema-change |
| 2. 双写准备 | 新旧结构并存,开启双写 | 应用代码改造,同时写入新旧库 |
| 3. 数据迁移 | 将旧数据同步到新分片结构 | ✅ ShardingSphere-Scaling(官方工具) ✅ DataX / Canal + 自定义脚本 |
| 4. 数据比对 | 校验新旧数据一致性 | 自定义比对脚本(按分片比对) |
| 5. 流量切换 | 逐步将读写流量切到新结构 | 灰度发布、路由规则调整 |
| 6. 回滚预案 | 准备回退方案 | 备份旧数据,监控异常 |
扩容示例(2 → 4 分片):
- 原规则:
ds_${user_id % 2},t_order_${user_id % 2} - 新规则:
ds_${user_id % 4},t_order_${user_id % 4} - 使用 一致性哈希 可减少数据迁移量
✅ 最佳实践:
- 使用 ShardingSphere-Scaling 实现在线迁移
- 迁移期间开启影子库验证新结构性能
- 避免在业务高峰期操作
13.5 版本升级与兼容性注意事项
| 项目 | 注意事项 | 建议 |
|---|---|---|
| 版本策略 | ShardingSphere 采用语义化版本(MAJOR.MINOR.PATCH) | 优先升级 PATCH 版本 |
| 配置兼容性 | YAML 配置格式可能变化(如 4.x → 5.x 变化大) | 仔细阅读升级指南 |
| SPI 接口变更 | 自定义算法可能因接口变动失效 | 升级前检查 SPI 接口是否兼容 |
| SQL 兼容性 | 新版本可能增强 SQL 解析,旧 SQL 行为可能变化 | 先在测试环境验证 |
| 依赖冲突 | 新版本可能升级底层依赖(如 Netty, HikariCP) | 检查 dependency:tree 避免冲突 |
| Proxy 协议兼容 | Proxy 对客户端协议兼容性较好 | 但仍需测试连接和查询 |
升级流程:
- 备份配置与数据
- 在测试环境验证新版本
- 检查 Breaking Changes
- 更新依赖并测试 SPI 扩展
- 灰度发布到生产
- 监控运行状态
✅ 推荐策略:
- 生产环境使用稳定版本(Stable Release)
- 避免直接跨大版本升级(如 4.x → 5.x)
- 关注官方公告和社区反馈
⚠️ 重要提示:升级前务必阅读官方发布的 Migration Guide。