ruoyi-vue-pro 开发指南 ruoyi-vue-pro 开发指南
  • 萌新必读
  • 后端手册
  • 中间件手册
  • 工作流手册
  • 大屏手册
  • 支付手册
  • 会员手册
  • 商城手册
  • ERP 手册
  • CRM 手册
  • MES 手册
  • WMS 手册
  • HRM 手册
  • FMS 手册
  • PMS 手册
  • OA 手册
  • IM 即时通讯手册
  • AI 大模型手册
  • IoT 物联网手册
  • 公众号手册
  • 系统手册
  • 运维手册
  • 前端手册 Vue 3.x
  • 前端手册 Vben 5.x
  • 前端手册 Vue 2.x
  • 前端手册 Admin Uniapp
  • 工作流手册
  • 大屏手册
  • 支付手册
  • 会员手册
  • 商城手册
  • ERP 手册
  • CRM 手册
  • MES 手册
  • WMS 手册
  • HRM 手册
  • FMS 手册
  • PMS 手册
  • OA 手册
  • IM 即时通讯手册
  • AI 大模型手册
  • IoT 物联网手册
  • 公众号手册
  • 系统手册
视频教程
  • Vue3 + element-plus (opens new window)
  • Vue3 + vben5(ant-design-vue、element-plus) (opens new window)
  • Vue2 + element-ui (opens new window)
微服务版 (opens new window)
作者博客 (opens new window)
GitHub (opens new window)
  • 萌新必读
  • 后端手册
  • 中间件手册
  • 工作流手册
  • 大屏手册
  • 支付手册
  • 会员手册
  • 商城手册
  • ERP 手册
  • CRM 手册
  • MES 手册
  • WMS 手册
  • HRM 手册
  • FMS 手册
  • PMS 手册
  • OA 手册
  • IM 即时通讯手册
  • AI 大模型手册
  • IoT 物联网手册
  • 公众号手册
  • 系统手册
  • 运维手册
  • 前端手册 Vue 3.x
  • 前端手册 Vben 5.x
  • 前端手册 Vue 2.x
  • 前端手册 Admin Uniapp
  • 工作流手册
  • 大屏手册
  • 支付手册
  • 会员手册
  • 商城手册
  • ERP 手册
  • CRM 手册
  • MES 手册
  • WMS 手册
  • HRM 手册
  • FMS 手册
  • PMS 手册
  • OA 手册
  • IM 即时通讯手册
  • AI 大模型手册
  • IoT 物联网手册
  • 公众号手册
  • 系统手册
视频教程
  • Vue3 + element-plus (opens new window)
  • Vue3 + vben5(ant-design-vue、element-plus) (opens new window)
  • Vue2 + element-ui (opens new window)
微服务版 (opens new window)
作者博客 (opens new window)
GitHub (opens new window)
  • 萌新必读

    • 简介
    • 交流群
    • 视频教程
    • 功能列表
    • 快速启动(后端项目)
    • 快速启动(前端项目)
    • 接口文档
    • 技术选型
    • 项目结构
    • 代码热加载
    • 一键改包
    • 迁移模块(适合新项目)
    • 删除功能(以租户为例)
    • 表结构变更(版本升级)
    • 国产信创数据库(DM 达梦、大金、OpenGauss、瀚高、OceanBase、TiDB、GaussDB、PolarDB、神通、Vastbase、MogDB、TongRDS、CacheDB)
    • 如何去除 Redis 缓存
    • 面试题、简历模版、简历优化
    • 项目外包
  • 后端手册

    • 新建模块
    • 代码生成【单表】(新增功能)
    • 代码生成【主子表】
    • 代码生成(树表)
    • 代码生成(移动端)
    • 功能权限
    • 数据权限
    • 用户体系
    • 三方登录
    • OAuth 2.0(SSO 单点登录)
    • SaaS 多租户【字段隔离】
    • SaaS 多租户【数据库隔离】
    • WebSocket 实时通信
    • 异常处理(错误码)
    • 参数校验、时间传参
    • 分页实现
    • VO 对象转换、数据翻译
    • 文件存储(上传下载)
    • Excel 导入导出
    • 操作日志、访问日志、异常日志
    • MyBatis 数据库
    • MyBatis 联表&分页查询
    • 多数据源(读写分离)、事务
    • Redis 缓存
    • 本地缓存
    • 异步任务
    • 分布式锁
    • 幂等性(防重复提交)
    • 请求限流(RateLimiter)
    • HTTP 接口签名(防篡改)
    • HTTP 接口加解密
    • 单元测试
    • 验证码
    • 工具类 Util
    • 配置管理
    • 数据库文档
  • 中间件手册

    • 定时任务
    • 消息队列(内存)
    • 消息队列(Redis)
    • 消息队列(RocketMQ)
    • 消息队列(RabbitMQ)
    • 消息队列(Kafka)
    • 限流熔断
    • 分库分表
      • 1. 方案说明
        • 1.1 整体结构
        • 1.2 示例表
      • 2. 获取集成材料
      • 3. 接入步骤
        • 3.1 初始化数据库
        • 3.2 添加依赖
        • 3.3 复制分片规则
        • 3.4 配置数据源
        • 3.5 复制示例代码
        • 3.6 启动验证
      • 4. 使用说明
        • 4.1 路由规则
        • 4.2 全局唯一主键
        • 4.3 多租户
        • 4.4 分页与排序
        • 4.5 事务
        • 4.6 接入自己的业务表
      • 5. 常见问题
        • 5.1 报 SnakeYAML 相关的 NoSuchMethodError 错误
        • 5.2 启动报 ShardingSphere 的 SPI、URL 类型找不到
        • 5.3 报错 Access denied for user
        • 5.4 报错 Table 'ruoyi-vue-pro.demo_sharding_order' doesn't exist
        • 5.5 报错 Table or view 'demo_sharding_order' does not exist
        • 5.6 Druid 监控中看不到分库分表的 SQL
        • 5.7 与早期分库分表方案的关系
    • 分布式事务
  • 工作流手册

    • 工作流演示
    • 功能开启
    • 工作流(达梦适配)
    • 审批接入(流程表单)
    • 审批接入(业务表单)
    • 流程设计器(BPMN)
    • 流程设计器(钉钉、飞书)
    • 选择审批人、发起人自选
    • 会签、或签、依次审批
    • 流程发起、取消、重新发起
    • 审批通过、不通过、驳回
    • 审批加签、减签
    • 审批转办、委派、抄送
    • 执行监听器、任务监听器
    • 流程表达式
    • 流程审批通知
    • 移动端审批
  • 大屏手册

    • 报表设计器
    • 大屏设计器
  • 支付手册

    • 功能开启
    • 支付宝支付接入
    • 微信公众号支付接入
    • 微信小程序支付接入
    • 支付宝、微信退款接入
    • 支付宝转账接入
    • 微信转账接入
    • 钱包充值、支付、退款
    • 模拟支付、退款
    • 移动端支付管理
  • 会员手册

    • 功能开启
    • 微信公众号登录
    • 微信小程序登录
    • 微信小程序订阅消息
    • 微信小程序码
    • 会员用户、标签、分组
    • 会员等级、积分、签到
    • 移动端会员管理
  • 商城手册

    • 商城演示
    • 功能开启
    • 商城装修
    • 在线客服
    • 【商品】商品分类
    • 【商品】商品属性
    • 【商品】商品 SPU 与 SKU
    • 【商品】商品评价
    • 【交易】购物车
    • 【交易】交易订单
    • 【交易】售后退款
    • 【交易】快递发货
    • 【交易】门店自提
    • 【交易】分销返佣
    • 【营销】优惠劵
    • 【营销】积分商城
    • 【营销】拼团活动
    • 【营销】秒杀活动
    • 【营销】砍价活动
    • 【营销】满减送活动
    • 【营销】限时折扣
    • 【营销】内容管理
    • 【统计】会员、商品、交易统计
    • 移动端商城管理
  • ERP手册

    • ERP 演示
    • 功能开启
    • 【产品】产品信息、分类、单位
    • 【库存】产品库存、库存明细
    • 【库存】其它入库、其它出库
    • 【库存】库存调拨、库存盘点
    • 【采购】采购订单、入库、退货
    • 【销售】销售订单、出库、退货
    • 【财务】采购付款、销售收款
    • 移动端 ERP
  • CRM手册

    • CRM 演示
    • 功能开启
    • 【线索】线索管理
    • 【客户】客户管理、公海客户
    • 【商机】商机管理、商机状态
    • 【合同】合同管理、合同提醒
    • 【回款】回款管理、回款计划
    • 【产品】产品管理、产品分类
    • 【通用】数据权限
    • 【通用】跟进记录、待办事项
    • 移动端 CRM
  • AI大模型手册

    • AI 大模型演示
    • 功能开启
    • AI 聊天对话
    • AI 绘画创作
    • AI 知识库(RAG)
    • AI 音乐创作
    • AI 写作助手
    • AI 思维导图
    • AI 工具(function calling)
    • AI 工作流
    • Dify 工作流
    • FastGPT 工作流
    • Coze 智能体
    • 推理模式(thinking)
    • 联网搜索
    • MCP Client 客户端
    • MCP Server 服务端
    • 【模型接入】Claude
    • 【模型接入】OpenAI
    • 【模型接入】通义千问
    • 【模型接入】DeepSeek
    • 【模型接入】字节豆包
    • 【模型接入】腾讯混元
    • 【模型接入】硅基流动
    • 【模型接入】MiniMax
    • 【模型接入】月之暗面
    • 【模型接入】百川智能
    • 【模型接入】文心一言
    • 【模型接入】LLAMA
    • 【模型接入】智谱 GLM
    • 【模型接入】讯飞星火
    • 【模型接入】微软 OpenAI
    • 【模型接入】谷歌 Gemini
    • 【模型接入】Stable Diffusion
    • 【模型接入】Midjourney
    • 【模型接入】Suno
    • 【模型接入】Grok
    • 【模型接入】阶跃星辰
    • 移动端 AI 大模型
  • IoT物联网手册

    • 功能开启
    • 产品管理
    • 设备管理
    • 物模型配置
    • 设备网关与子设备
    • 设备动态注册
    • 设备影子
    • 消息透传与数据解析
    • 设备接入(概述)
    • 设备接入(HTTP 协议)
    • 设备接入(MQTT 协议)
    • 设备接入(EMQX 协议)
    • 设备接入(TCP 协议)
    • 设备接入(UDP 协议)
    • 设备接入(WebSocket 协议)
    • 设备接入(CoAP 协议)
    • 设备接入(Modbus Client 模式)
    • 设备接入(Modbus Server 模式)
    • 设备接入(自定义协议)
    • 场景联动
    • 数据流转
    • 告警配置
    • OTA 固件升级
    • 移动端 IoT 物联网
  • MES手册

    • MES 演示
    • 功能开启
    • 【基础】物料产品、分类、计量单位
    • 【基础】客户管理、供应商管理
    • 【基础】车间设置、工作站设置
    • 【基础】编码规则
    • 【生产】工序设置、工艺流程
    • 【生产】生产工单
    • 【生产】生产排产、工序流转卡
    • 【生产】生产报工
    • 【生产】安灯配置、安灯呼叫
    • 【生产】工作记录
    • 【仓库】仓库与库区库位、条码赋码、SN码
    • 【仓库】批次管理、库存现有量、库存事务
    • 【仓库】到货通知、采购入库、采购退货
    • 【仓库】生产领料、生产退料、物料消耗
    • 【仓库】产品产出、产品入库
    • 【仓库】发货通知、销售出库、销售退货
    • 【仓库】外协发料、外协入库
    • 【仓库】其他入库、其他出库
    • 【仓库】调拨单、装箱管理
    • 【仓库】库存盘点
    • 【质量】检测项设置、常见缺陷
    • 【质量】质检方案
    • 【质量】来料检验(IQC)
    • 【质量】过程检验(IPQC)
    • 【质量】出货检验(OQC)
    • 【质量】退货检验(RQC)
    • 【质量】待检任务、检验结果、缺陷记录
    • 【设备】设备类型、设备台账
    • 【设备】点检保养项目、点检保养方案
    • 【设备】点检记录、保养记录、维修单
    • 【工具】工具类型、工装夹具台账
    • 【排班】班组设置、节假日设置
    • 【排班】排班计划、排班日历
    • 移动端 MES
  • WMS手册

    • WMS 演示
    • 功能开启
    • 【基础】仓库
    • 【基础】商品、SKU、分类、品牌
    • 【基础】往来企业(供应商、客户)
    • 【库存】库存记录、流水、统计
    • 【单据】入库
    • 【单据】出库
    • 【单据】移库
    • 【单据】盘库
    • 【其它】WMS、MES、ERP 对比
    • 移动端 WMS
  • HRM人力资源

    • HRM 演示
    • 功能开启
    • 【组织】工作台、组织架构
    • 【员工】员工管理
    • 【招聘】招聘管理
    • 【考勤】考勤管理
    • 【社保】社保管理
    • 【薪资】计薪设置、薪资档案
    • 【薪资】月度工资、工资条
    • 【绩效】绩效模板、绩效计划
    • 【绩效】绩效考核、绩效档案
    • 移动端 HRM
  • FMS财务管理

    • FMS 演示
    • 功能开启
    • 【设置】账套管理、财务参数、财务指标
    • 【设置】币别、科目、辅助核算、初始余额
    • 【设置】凭证字、常用摘要、凭证模板
    • 【凭证】凭证管理
    • 【账簿】账簿管理
    • 【报表】财务报表
    • 【结账】期末结账
    • 移动端 FMS
  • PMS项目管理

    • PMS 演示
    • 功能开启
    • 【项目中心】工作台与项目管理
    • 【项目中心】项目详情与迭代
    • 【项目中心】工作项与协作
    • 【知识中心】知识库管理
    • 【知识中心】文档与协作
    • 移动端 PMS
  • OA协同办公

    • OA 演示
    • 功能开启
    • 【协作】日程、任务、计划与汇报
    • 【协作】公告、讨论、通讯录与笔记
    • 【办公】企业邮箱
    • 【办公】企业云盘
    • 【行政】办公用品、用印管理
    • 【行政】会议室、车辆管理
    • 【流程】公文管理
    • 【流程】出差、费用报销
    • 【流程】考勤、请假、加班、转正与离职
    • 移动端 OA
  • IM即时通讯手册

    • IM 演示
    • 功能开启
    • 【好友】好友关系、好友申请
    • 【群聊】群组、群成员、入群申请
    • 【消息】私聊、群聊、频道消息
    • 【频道】频道、频道素材
    • 【内容】表情、敏感词
    • 【通话】语音通话、视频通话、共享屏幕
    • 【WebSocket】实时推送与离线消息
    • 移动端 IM 即时通讯
  • 公众号手册

    • 功能开启
    • 公众号接入
    • 公众号粉丝
    • 公众号标签
    • 公众号消息
    • 模版消息
    • 自动回复
    • 公众号菜单
    • 公众号素材
    • 公众号图文
    • 公众号统计
    • 移动端公众号管理
  • 系统手册

    • 短信配置
    • 邮件配置
    • 站内信配置
    • Webhook(钉钉、飞书、企微)
    • 数据脱敏、字段权限
    • 敏感词
    • 地区 & IP 库
  • 运维手册

    • 开发环境
    • Linux 部署
    • Docker 部署
    • Jenkins 部署
    • 宝塔部署
    • 1Panel 部署
    • Docker Compose 部署
    • Kubernetes 部署
    • HTTPS 证书
    • 服务监控
    • Tomcat WAR 部署
    • 东方通服务器部署(TongWeb、TongHttpServer)
    • 宝兰德服务器部署(BES AppServer)
    • 内网穿透
  • 前端手册 Vue 3.x

    • 开发规范
    • 菜单路由
    • Icon 图标
    • 字典数据
    • 系统组件
    • 通用方法
    • 配置读取
    • CRUD 组件
    • 国际化
    • IDE 调试
    • 代码格式化
  • 前端手册 Vben 5.x

    • 开发规范
    • 菜单路由
    • 图标、主题、国际化
    • 字典数据
    • 系统组件
    • 通用方法
    • 配置读取
    • IDE 调试
    • 代码格式化
  • 前端手册 Vue 2.x

    • 开发规范
    • 菜单路由
    • Icon 图标
    • 字典数据
    • 系统组件
    • 通用方法
    • 配置读取
  • 前端手册 Admin Uniapp

    • 开发规范
    • 菜单路由
    • 图标、主题、国际化
    • 字典数据
    • 系统组件
    • 通用方法
    • IDE 调试
    • 代码格式化
    • 运行发布
  • 更新日志

    • 【v2026-10】
    • 【v2026-09】
    • 【v2026-08】
    • 【v2026-07】
    • 【v2026-06】
    • 【v2026-05】
    • 【v2026-04】
    • 【v2026-03】
    • 【v2026-01】
    • 【v2025-12】
    • 【v2025-11】
    • 【v2025-10】
    • 【v2025-09】
    • 【v2025-08】
  • 开发指南
  • 中间件手册
芋道源码
2026-10-24
目录

分库分表

项目默认不开启分库分表。单表数据量确实过大时,可以基于 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 算法)、预建物理表,并规划历史数据迁移。

限流熔断
分布式事务

← 限流熔断 分布式事务→

Theme by Vdoing | Copyright © 2019-2026 芋道源码 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×