分库分表
项目默认不开启分库分表。单表数据量确实过大时,可以基于 Apache ShardingSphere-JDBC (opens new window) 按需接入,实际使用的依赖版本见《3.2 添加依赖》小节。
接入方式是在 《多数据源(读写分离)》 的基础上,新增一个名为 sharding 的数据源:
master主库保持不变,仍然是默认数据源。工作流、普通业务等 SQL,不会走分库分表。- 只有添加了
@DS("sharding")注解的 Mapper、Service,才通过ShardingSphereDriver访问分库分表的逻辑表。
友情提示:
分库分表会带来跨库事务、跨分片查询、数据迁移等成本。接入前,优先考虑索引优化、历史数据归档、读写分离等方案。
# 1. 方案说明
# 1.1 整体结构
业务代码 @DS("sharding")
└── Dynamic Datasource 的 sharding 数据源(HikariCP 连接池,逻辑连接)
└── ShardingSphereDriver,加载 classpath 下的 sharding/sharding.yaml 规则
├── ds_0:yudao_sharding_0 库(HikariCP 连接池)
│ ├── demo_sharding_order_0
│ └── demo_sharding_order_1
└── ds_1:yudao_sharding_1 库(HikariCP 连接池)
├── demo_sharding_order_0
└── demo_sharding_order_1
# 1.2 示例表
项目提供了一张独立的示例逻辑表 demo_sharding_order,不会把已有的业务表切成分片。分片规则如下:
| 项目 | 说明 |
|---|---|
| 逻辑表 | demo_sharding_order,即 DO 上 @TableName 的表名,物理库中不存在这张表 |
| 分片键 | user_id,分库、分表使用同一个分片键 |
| 分库算法 | ds_${user_id % 2} |
| 分表算法 | demo_sharding_order_${user_id.intdiv(2) % 2},即 user_id 整除 2 后对 2 取余,与分库错开 |
| 实际节点 | ds_0、ds_1 两个库 × 每库 demo_sharding_order_0、demo_sharding_order_1 两张表,共 4 个 |
| 主键 | MyBatis Plus 的 IdType.ASSIGN_ID 雪花算法,由应用生成,物理表不使用自增 |
| 多租户 | 物理表有 tenant_id 字段,由项目的多租户插件自动拼接条件 |
例如说,user_id 为 1 ~ 4 的订单,分别路由到 4 个不同的节点:
| user_id | 物理库 | 物理表 |
|---|---|---|
| 1 | ds_1(yudao_sharding_1) | demo_sharding_order_0 |
| 2 | ds_0(yudao_sharding_0) | demo_sharding_order_1 |
| 3 | ds_1(yudao_sharding_1) | demo_sharding_order_1 |
| 4 | ds_0(yudao_sharding_0) | demo_sharding_order_0 |
为什么不直接使用已有的业务表做示例?
以 API 访问日志 infra_api_access_log 为例:它使用数据库自增主键,分表后各表会生成重复编号;它的定时清理使用 DELETE ... LIMIT 语句,分片后会在每个分片上各自执行;管理后台的分页又不带分片键,每次都需要查询全部分片。此外,主库中已有的历史数据也需要迁移。所以,示例使用独立的新表,已有业务表的改造需要按本文第 4 节自行评估。
# 2. 获取集成材料
集成材料在项目的 script/sharding/ (opens new window) 目录,从你所使用的项目代码中获取即可。各分支的接入步骤相同,只需按当前分支选择兼容的依赖:
| 分支 | JDK | Spring Boot | 差异 |
|---|---|---|---|
master | 8 | 2.7.18 | 添加 SnakeYAML 1.33,见《3.2 添加依赖》小节 |
master-jdk17 | 17 | 3.5.15 | 添加 SnakeYAML 2.7,见《3.2 添加依赖》小节 |
master-jdk25 | 25 | 4.1.2 | 添加 SnakeYAML 2.7,见《3.2 添加依赖》小节 |
目录中的文件及用途如下:
| 文件 | 说明 | 目标位置 |
|---|---|---|
sql/mysql/demo_sharding_order.sql | 创建 2 个物理库、4 张物理表 | 在 MySQL 中执行 |
dependencies.xml | Maven 依赖片段 | yudao-server/pom.xml |
dynamic-datasource.yaml | sharding 数据源配置片段 | yudao-server 的 application-local.yaml 等配置文件 |
src/main/resources/sharding/sharding.yaml | ShardingSphere 分片规则 | yudao-module-infra/src/main/resources/sharding/ |
src/main/java/.../shardingdemo/ | 示例 DO、Mapper、Service | yudao-module-infra/src/main/java/ 的同名包 |
# 3. 接入步骤
# 3.1 初始化数据库
在 MySQL 中执行 sql/mysql/demo_sharding_order.sql 脚本,创建 yudao_sharding_0、yudao_sharding_1 两个库,以及每个库下的 demo_sharding_order_0、demo_sharding_order_1 两张表。
- 脚本使用「库名.表名」的写法,可以在同一个 MySQL 实例上直接执行。如果两个库在不同的 MySQL 实例上,分别执行对应库的语句即可。
- 需要在启动项目之前建表。ShardingSphere 在初始化时加载物理表的元数据,启动后才建的表,需要重启项目后才能识别。
- 不要在
master主库中创建同名的demo_sharding_order表。这样一旦数据源切换失效、SQL 落到了主库,会直接报表不存在的错误,而不是“静默”写入主库。
# 3.2 添加依赖
将 dependencies.xml 中的依赖,复制到 yudao-server 的 pom.xml 中:
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc</artifactId> <!-- 内核,提供 ShardingSphereDriver -->
<version>5.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-sharding-core</artifactId> <!-- 数据分片功能,即 !SHARDING 规则 -->
<version>5.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-dialect-mysql</artifactId> <!-- MySQL 的 SQL 解析、路由、改写 -->
<version>5.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-infra-url-classpath</artifactId> <!-- 支持 jdbc:shardingsphere:classpath: 加载规则文件 -->
<version>5.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-standalone-mode-repository-memory</artifactId> <!-- 单机模式的 Memory 存储 -->
<version>5.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-infra-data-source-pool-hikari</artifactId> <!-- 物理库使用 HikariCP 连接池 -->
<version>5.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-authority-simple</artifactId> <!-- 默认的 ALL_PERMITTED 权限实现,缺少时初始化报 SPI-00001 -->
<version>5.5.3</version>
</dependency>
ShardingSphere 5.5.x 是插件化的,shardingsphere-jdbc 只包含内核,分片功能、MySQL 方言、规则文件加载、单机模式存储、连接池适配、默认权限实现都需要显式引入,请完整复制。不要使用网上 5.0 ~ 5.2 版本的 shardingsphere-jdbc-core-spring-boot-starter 写法,它最后的版本是 5.2.1,之后官方不再提供 Spring Boot Starter。
依赖需要添加在 yudao-server 这个最终打包启动的模块中。Groovy、Caffeine、Jackson、HikariCP 等传递依赖的版本,继续由项目的 Spring Boot BOM 管理,无需额外覆盖。
然后,按项目所在分支,只选择一个 SnakeYAML 依赖,同样添加到 yudao-server 的 pom.xml 中。两个版本不能同时添加:
| 分支 | Spring Boot 管理的 SnakeYAML | 添加的 SnakeYAML | 对应 dependencies.xml |
|---|---|---|---|
master | 1.30 | 1.33 | 第 2 段 |
master-jdk17 | 2.4 | 2.7 | 第 3 段 |
master-jdk25 | 2.6 | 2.7 | 第 3 段 |
① master-jdk17、master-jdk25 分支,添加 SnakeYAML 2.7:
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.7</version>
</dependency>
Spring Boot BOM 管理的 2.4、2.6 也可以直接使用,这里显式升级到 2.7。ShardingSphere 规则加载、Spring Boot 配置加载,以及 BOM 管理的新版 jackson-dataformat-yaml 都能使用。
② master(JDK 8)分支,添加 SnakeYAML 1.33:
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
Spring Boot 2.7.18 管理的 SnakeYAML 是 1.30,缺少 ShardingSphere 调用的 LoaderOptions#setCodePointLimit 方法(1.32 起提供),会在初始化 sharding 数据源时报 NoSuchMethodError 错误。1.33 是 SnakeYAML 1.x 的最后一个版本,ShardingSphere 和项目已有的 YAML 解析都能使用。
为什么 master 分支不能也升级到 SnakeYAML 2.7?
2.x 删除了 ParserImpl(StreamReader) 构造方法,而 master 分支由 Spring Boot 2.7.18 管理的 jackson-dataformat-yaml 是 2.13.5,仍然调用它,单独升级 SnakeYAML 到 2.7 后,Jackson 解析 YAML 时会报 NoSuchMethodError 错误。
jackson-dataformat-yaml 从 2.15 起才改用 SnakeYAML 2.x,这需要整体升级项目的 Jackson 版本,影响全站的 JSON 序列化,不在本文的接入范围内。所以,master 分支请保持 1.33。
# 3.3 复制分片规则
将 src/main/resources/sharding/sharding.yaml 复制到 yudao-module-infra/src/main/resources/sharding/ 目录下。它是 ShardingSphere 自己的规则文件,核心内容如下:
dataSources:
ds_0:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: $${YUDAO_SHARDING_DS0_URL::jdbc:mysql://127.0.0.1:3306/yudao_sharding_0?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true}
username: $${YUDAO_SHARDING_DS0_USERNAME::root}
password: '$${YUDAO_SHARDING_DS0_PASSWORD::}'
minimumIdle: 1
maximumPoolSize: 10
ds_1:
# ... 与 ds_0 相同,环境变量为 YUDAO_SHARDING_DS1_*
rules:
- !SHARDING
tables:
demo_sharding_order:
actualDataNodes: ds_$->{0..1}.demo_sharding_order_$->{0..1}
databaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: demo_sharding_order_database_inline
tableStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: demo_sharding_order_table_inline
shardingAlgorithms:
demo_sharding_order_database_inline:
type: INLINE
props:
algorithm-expression: ds_$->{user_id % 2}
demo_sharding_order_table_inline:
type: INLINE
props:
algorithm-expression: demo_sharding_order_$->{user_id.intdiv(2) % 2}
物理库的账号密码,通过环境变量传入,不要写入规则文件、提交到 Git:
| 环境变量 | 说明 | 默认值 |
|---|---|---|
YUDAO_SHARDING_DS0_URL | ds_0 的 JDBC URL | jdbc:mysql://127.0.0.1:3306/yudao_sharding_0?... |
YUDAO_SHARDING_DS0_USERNAME | ds_0 的账号 | root |
YUDAO_SHARDING_DS0_PASSWORD | ds_0 的密码 | 无,必须设置 |
YUDAO_SHARDING_DS1_URL | ds_1 的 JDBC URL | jdbc:mysql://127.0.0.1:3306/yudao_sharding_1?... |
YUDAO_SHARDING_DS1_USERNAME | ds_1 的账号 | root |
YUDAO_SHARDING_DS1_PASSWORD | ds_1 的密码 | 无,必须设置 |
本地开发时,在 IDEA 启动 YudaoServerApplication 的 Run Configuration 中,设置 Environment variables 即可;服务器部署时,在启动脚本或容器中设置。
关于 `$${环境变量名::默认值}` 占位符:
① 规则文件由 ShardingSphere 自己解析,不支持 Spring 的 ${ENV:default} 占位符。
② 它依赖 sharding 数据源 URL 上的 placeholder-type=environment 参数(见下一小节),去掉后占位符不会被替换。
③ 环境变量名与默认值之间的 :: 不能省略,没有默认值时写成 $${环境变量名::}。环境变量未设置或者为空时,使用默认值。
④ 环境变量的值会原样替换到规则文件中,所以密码不能包含 $、\、' 字符。否则,可以改用 jdbc:shardingsphere:absolutepath: 加载服务器上、不在 Git 中的规则文件,需要额外引入 shardingsphere-infra-url-absolutepath 依赖。
# 3.4 配置数据源
将 dynamic-datasource.yaml 中的 sharding 数据源,合并到 yudao-server 的 application-local.yaml 配置文件中,与已有的 master、slave 同级:
spring:
datasource:
dynamic:
datasource:
# master、slave 保持原有配置不变,在它们的同级新增 sharding 即可
sharding: # 分库分表数据源,只有 @DS("sharding") 的 Mapper、Service 使用,不影响 master 主库
lazy: false # 启动时初始化,规则或数据库有误时尽早失败;也避免在异步线程中首次加载 classpath 规则文件
type: com.zaxxer.hikari.HikariDataSource # 不使用 Druid 连接池,ShardingSphere 的逻辑 SQL 不需要 Druid 解析
driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
url: jdbc:shardingsphere:classpath:sharding/sharding.yaml?placeholder-type=environment # 规则文件中的 $${环境变量名::默认值},从环境变量读取
hikari:
minimum-idle: 1
maximum-pool-size: 10 # 逻辑连接数,不能大于 sharding.yaml 中每个物理库的 maximumPoolSize
- 不要修改
primary: master,也不要把sharding设置为默认数据源,否则工作流、普通业务的 SQL 都会经过 ShardingSphere。 - 这里不配置
username、password,物理库的账号密码以规则文件为准。 - 使用其它环境(例如说
application-dev.yaml)时,在对应的配置文件中同样添加。
# 3.5 复制示例代码
将 src/main/java 下的 cn.iocoder.yudao.module.infra.shardingdemo 包,复制到 yudao-module-infra/src/main/java 的同名目录,包含如下 3 个类:
DemoShardingOrderDO:继承TenantBaseDO,主键使用@TableId(type = IdType.ASSIGN_ID)。DemoShardingOrderMapper:在 Mapper 接口上添加@DS("sharding")注解,保证这张逻辑表的 SQL 都使用sharding数据源。DemoShardingOrderService:在类上也添加@DS("sharding")注解,先切换数据源、再开启@Transactional事务,提供新增、修改、删除、单分片查询、全分片分页等示例方法。
示例代码只依赖项目已有的 MyBatis Plus、Dynamic Datasource 和多租户组件,不直接引用 ShardingSphere 的类,并使用构造方法注入,三个分支均无需修改。
# 3.6 启动验证
新增一个临时的 ApplicationRunner,在项目启动完成后,在租户编号为 1 的上下文中调用示例 Service:
package cn.iocoder.yudao.module.infra.shardingdemo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
/**
* 分库分表示例:启动后验证路由,验证完成后删除
*
* @author 芋道源码
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class DemoShardingOrderRunner implements ApplicationRunner {
private final DemoShardingOrderService demoShardingOrderService;
@Override
public void run(ApplicationArguments args) {
TenantUtils.execute(1L, () -> {
for (long userId = 1; userId <= 4; userId++) {
Long id = demoShardingOrderService.createOrder(userId, "分库分表示例-" + userId);
log.info("[新增] userId = {}, id = {}", userId, id);
}
log.info("[单分片] userId = 1, size = {}", demoShardingOrderService.getOrderListByUserId(1L).size());
PageResult<DemoShardingOrderDO> page = demoShardingOrderService.getOrderPage(new PageParam(), null);
log.info("[全分片分页] total = {}, size = {}", page.getTotal(), page.getList().size());
});
}
}
设置好《3.3 复制分片规则》小节的环境变量后,启动后端项目。首次执行时,预期的日志如下(编号以实际生成的为准):
dynamic-datasource - add a datasource named [sharding] success
[新增] userId = 1, id = 1981234567890123457
[新增] userId = 2, id = 1981234567890123458
[新增] userId = 3, id = 1981234567890123459
[新增] userId = 4, id = 1981234567890123460
[单分片] userId = 1, size = 1
[全分片分页] total = 4, size = 4
然后,直接连接 MySQL,确认 4 条记录分别落在 4 个物理节点,与《1.2 示例表》小节的路由表一致:
SELECT 'ds_0.demo_sharding_order_0' AS node, user_id FROM yudao_sharding_0.demo_sharding_order_0
UNION ALL SELECT 'ds_0.demo_sharding_order_1', user_id FROM yudao_sharding_0.demo_sharding_order_1
UNION ALL SELECT 'ds_1.demo_sharding_order_0', user_id FROM yudao_sharding_1.demo_sharding_order_0
UNION ALL SELECT 'ds_1.demo_sharding_order_1', user_id FROM yudao_sharding_1.demo_sharding_order_1;
验证完成后,记得删除 DemoShardingOrderRunner 类,否则每次启动都会插入记录。不再需要示例时,删除 shardingdemo 包即可,sharding 数据源可以保留给自己的业务表使用。
如果想查看每条 SQL 实际路由到了哪个节点,可以临时将 sharding.yaml 中的 props.sql-show 设置为 true,日志中会打印 Logic SQL 和 Actual SQL。它会打印 SQL 的参数值,生产环境不要开启。
# 4. 使用说明
# 4.1 路由规则
| SQL 条件 | 路由结果 | 示例方法 |
|---|---|---|
user_id = ? | 1 个库的 1 张表 | getOrder、getOrderListByUserId、updateOrderName、deleteOrder |
user_id IN (?, ?) | 涉及的节点 | - |
没有 user_id 条件,例如只有 id = ? | 全部 4 个节点,再归并结果 | getOrderPage 的 userId 为空时 |
user_id 范围查询,例如 BETWEEN | 报错 inline sharding algorithm can not tackle with range query | - |
① 新增时,user_id 必须有值,否则 ShardingSphere 无法确定写入哪个节点。
② 修改时,不要修改 user_id,示例的 updateOrderName 方法只更新名称、在条件中带上 user_id。如果确实需要“换分片”,需要先删除、再在新的分片中新增。
③ 只根据 id 查询、修改、删除时,会在全部节点执行。高频场景下,建议接口参数中同时带上分片键,或者自行维护 id 与 user_id 的映射关系。
④ INLINE 分片算法默认不允许范围查询。如果确实需要,可以在算法的 props 中设置 allow-range-query-with-inline-sharding: true,此时会在全部节点执行。
# 4.2 全局唯一主键
分库分表后,每张物理表的自增编号相互独立,会出现重复,所以示例主键使用 MyBatis Plus 的 IdType.ASSIGN_ID 雪花算法,在应用中生成,物理表不使用 AUTO_INCREMENT。
- 雪花算法的编号超过 JavaScript 的安全整数范围。项目的 Jackson 配置会将超出范围的
Long序列化为字符串,前端按字符串处理即可。 - MyBatis Plus 默认根据 MAC 地址、进程号推导机器编号,多实例部署时,重复的概率很低但不为零。对唯一性要求严格时,需要自定义
IdentifierGenerator,为每个实例分配不同的机器编号。 - 数据库主键只能保证单张物理表内唯一,无法发现跨节点的重复编号。
# 4.3 多租户
DemoShardingOrderDO 继承了 TenantBaseDO,项目的多租户插件会在每条 SQL 上拼接 tenant_id 条件,包括查询全部分片的 SQL,所以不同租户的数据不会相互读取。
注意,这是字段级的租户隔离:分片键是 user_id,同一个租户的数据会分布在多个节点上。如果需要按租户进行物理隔离,请阅读 《SaaS 多租户【数据库隔离】》 文档,它与本文是不同的方案。
不需要多租户的表,可以继承 BaseDO,并添加 @TenantIgnore 注解。
# 4.4 分页与排序
示例的 getOrderPage 方法,使用项目已有的 selectPage 分页,并按 id 倒序:
- MyBatis Plus 分页插件会根据实际物理库的 JDBC URL,识别为 MySQL,生成
LIMIT分页语句。count 总数、列表数据在同一次selectPage调用中执行,都使用sharding数据源。 - 带
user_id时,只查询 1 个节点。不带时,ShardingSphere 将LIMIT offset, size改写为每个节点查询LIMIT 0, offset + size,再归并排序。页码越大,每个节点返回的数据越多,深分页时建议改为“上一页最后一条的id”作为条件的游标分页。 - 排序字段需要能确定唯一顺序,例如说
id。只按create_time排序时,相同时间的记录在不同节点之间的顺序不稳定。
# 4.5 事务
在 @DS("sharding") 的方法上添加 @Transactional 注解,使用的是 ShardingSphere 的本地事务(LOCAL):
- 操作只涉及同一个物理库时,例如说同一个
user_id的多条记录,等同于普通的单库事务。 - 操作涉及多个物理库时,ShardingSphere 会逐个物理库提交。如果某个库提交失败,已经提交的库不会回滚,不保证跨库原子性。
- 项目默认不接入 XA、Seata 分布式事务。对跨库一致性要求高的场景,建议通过分片键设计让写操作落在同一个库,或者设计补偿逻辑。
另外,和 《多数据源(读写分离)》 的 @Transactional 规则一致:外层方法已经在 master 上开启了事务时,内层的 @DS("sharding") 不会生效。
# 4.6 接入自己的业务表
在示例的基础上,接入自己的业务表时:
① 选择分片键:优先选择查询、写入时都会带上的字段,并且数据分布均匀,例如说用户编号、订单的买家编号。
② 在各个物理库中创建物理表,表结构完全一致,主键不使用自增。
③ 在 sharding.yaml 的 tables 中添加逻辑表规则,物理库较多时,在 dataSources 中添加对应的数据源。
④ DO 的主键使用 @TableId(type = IdType.ASSIGN_ID),Mapper 接口上添加 @DS("sharding") 注解。
⑤ 不要在 master 主库中保留同名的逻辑表。
本文只提供新表的接入方式,不包含历史数据迁移与扩容。已有表的数据迁移、双写校验,以及后续增加库表数量时的数据重新分布,需要根据业务自行规划和演练,项目不会自动迁移业务数据。
除 MySQL 外的数据库,需要更换 shardingsphere-jdbc-dialect-* 方言依赖、JDBC 驱动和建表 SQL,项目暂未验证。
# 5. 常见问题
# 5.1 报 SnakeYAML 相关的 NoSuchMethodError 错误
SnakeYAML 的版本与分支不匹配,参见《3.2 添加依赖》小节:
NoSuchMethodError: LoaderOptions.setCodePointLimit:master(JDK 8)分支没有添加 SnakeYAML 1.33,仍在使用 Spring Boot 管理的 1.30。NoSuchMethodError: ParserImpl.<init>(StreamReader):master(JDK 8)分支添加了 SnakeYAML 2.x(例如说 2.7),与 Jackson 2.13.5 的 YAML 解析不兼容,改为 1.33。
确认 dependencies.xml 第 2、3 段的 SnakeYAML 只添加了其中一个,可以通过 mvn dependency:tree -Dincludes=org.yaml:snakeyaml 查看实际使用的版本。
# 5.2 启动报 ShardingSphere 的 SPI、URL 类型找不到
例如说 !SHARDING 规则无法识别、classpath: 不支持、Memory 存储找不到等,通常是缺少 dependencies.xml 中的某个依赖。5.5.x 的依赖需要全部引入。
其中,报错 SPI-00001: No implementation class load from SPI 'org.apache.shardingsphere.authority.spi.PrivilegeProvider' with type 'ALL_PERMITTED' 时,是缺少 shardingsphere-authority-simple 依赖。
# 5.3 报错 Access denied for user
物理库的账号密码不正确。确认环境变量设置在 yudao-server 进程上,例如说 IDEA 的 Run Configuration,而不是 application-local.yaml 中;并确认 URL 带有 placeholder-type=environment 参数,密码不包含 $、\、' 字符。
# 5.4 报错 Table 'ruoyi-vue-pro.demo_sharding_order' doesn't exist
SQL 落到了 master 主库,说明数据源切换没有生效。排查方式见 《多数据源(读写分离)》 的常见问题,常见原因有:
- 启动日志中没有
add a datasource named [sharding] success,sharding数据源没有配置在当前生效的配置文件中。 - 外层方法已经在
master上开启了@Transactional事务。 - 通过
this.xxx()自调用,或者在其它线程中执行,@DS注解没有被拦截。
# 5.5 报错 Table or view 'demo_sharding_order' does not exist
ShardingSphere 没有加载到物理表的元数据。确认 4 张物理表都已经创建,表名与 actualDataNodes 一致,并在建表后重启项目。
# 5.6 Druid 监控中看不到分库分表的 SQL
sharding 数据源使用 HikariCP 连接池,不经过 Druid,属于正常现象。可以临时开启 sql-show,参见《3.6 启动验证》小节。
# 5.7 与早期分库分表方案的关系
早期的相关讨论,见 yudao-cloud PR #136 (opens new window),感谢作者 Qzzzzzzzp (opens new window) 的探索。它基于 5.2.1 版本的 shardingsphere-jdbc-core-spring-boot-starter,并覆盖了 SnakeYAML 官方的 Representer 类。本文没有采用这些代码,而是改用 5.5.3 官方 ShardingSphereDriver + 项目现有 Dynamic Datasource 的可选接入,YAML 兼容、MyBatis Plus 分页及跨分片 count 已经实际验证。
另外,该 PR 将操作日志 system_operate_log 按 create_time 按月分表,而本文的独立示例按 user_id 分片,不包含这一改造,项目现有的日志业务也保持不变。如需按月分表,需要自行设计分片算法(例如说 ShardingSphere 内置的 INTERVAL 算法)、预建物理表,并规划历史数据迁移。