一,ShardingSphere 介绍
Apache ShardingSphere 是一个开源的分布式数据库生态系统,简单来说,它处于你的应用和数据库之间,通过构建一层代理或增强,帮你把复杂的数据库操作变得简单高效。
它能解决什么问题?
随着业务发展,数据量激增,一个数据库往往难以应付。ShardingSphere 主要通过以下方式解决这个难题:
- 应对数据暴涨:当一张表的数据量达到千万级甚至亿级时,查询会变慢。ShardingSphere 可以将一张表的数据水平拆分,分散存储到多个数据库或表中,突破单机的性能瓶颈。
- 突破并发瓶颈:当数据库每秒处理的请求数(QPS)过高时,它可以将请求分散到多个数据库实例上,成倍提升系统的吞吐能力。
- 提升系统可用性:通过分库,将数据分散部署,单一节点故障只影响部分数据,避免了"一个人感冒,全公司请假"的局面,提高了系统整体的可用性。
ShardingSphere 的目标就是让你像操作一个数据库一样,轻松管理成百上千个数据库实例。
核心特性一览
ShardingSphere 功能丰富,远不止分库分表,官网列出了以下核心能力:
- 数据分片:其看家本领,通过灵活的分片策略,透明化地实现分库分表,解决了海量数据存储和高并发访问的瓶颈。
- 读写分离:自动将"写"操作指向主库,"读"操作负载均衡地分发到从库,有效提升系统的整体吞吐量。
- 分布式事务:提供了 XA 强一致事务和 BASE 柔性事务两种模式,让你在数据分散后,依然能保证跨库操作的数据一致性。
- 数据加密:支持对数据库中的敏感字段(如身份证、手机号)进行透明的加密存储,业务代码无需改动即可实现数据安全防护。
- SQL 联邦查询:支持跨多个数据源进行复杂的联合查询,简化数据聚合的难度。
- 云原生与多数据库支持:完美适配 MySQL、PostgreSQL、openGauss 等多种主流数据库,并能无缝集成到云原生环境中。
产品形态
ShardingSphere 不是一个单一产品,而是由三部分组成,你可以根据场景灵活选择:
| 产品形态 | 工作方式 | 一句话总结适用场景 |
|---|---|---|
| Sharding-JDBC | 以 JAR包 形式嵌入你的 Java 应用,在客户端进行SQL路由和改写。 | Java 开发者首选,轻量级,性能最高,没有额外的中间件部署开销。 |
| Sharding-Proxy | 一个独立的 代理服务,对应用透明。应用像连接普通数据库一样连接它即可。 | 适合异构语言,或希望 DBA 统一进行运维管理的场景,对业务代码零侵入。 |
| Sharding-Sidecar | 以 Sidecar 容器 形式运行,是云原生架构下的数据面。 | 面向未来的 云原生架构,适用于 Kubernetes 等容器环境。 |
二,快速入门(JDBC方式)
Maven依赖
<!-- Spring Boot 集成 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.5.0</version>
</dependency>
配置文件 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/db0
username: root
password: 123456
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db1
username: root
password: 123456
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: table-mod
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: db-mod
sharding-algorithms:
db-mod:
type: MOD
props:
sharding-count: 2
table-mod:
type: MOD
props:
sharding-count: 2
props:
sql-show: true # 显示SQL解析日志
代码使用(完全透明)
@Repository
public interface OrderMapper {
@Insert("INSERT INTO t_order(order_id, user_id, status) VALUES(#{orderId}, #{userId}, #{status})")
void insert(Order order);
@Select("SELECT * FROM t_order WHERE order_id = #{orderId}")
Order selectById(Long orderId);
}
无需修改 SQL,ShardingSphere 自动路由到正确分片。
三,配置详解
3.1 整体配置框架
一个标准的 ShardingSphere YAML 配置,顶层由四大块组成:
# 1. 运行模式
mode:
# ...
# 2. 数据源定义
dataSources:
# ...
# 3. 规则定义(核心)
rules:
# ...
# 4. 全局属性
props:
# ...
下面按照这个目录顺序,逐一展开。
3.2 mode(运行模式)
mode:
type: Standalone # 运行模式类型
repository: # 持久化仓库配置
type: File # 仓库类型
props:
path: .shardingsphere/.meta # 元数据文件路径
mode 决定三件事:
- 元数据存哪里(分片规则、数据源配置等)
- 是否需要集群协调(多实例间配置同步、主节点选举)
- 配置如何下发
先看一个总览表:
| mode.type | 依赖组件 | 适用场景 | 元数据存储 |
|---|---|---|---|
Memory | 无 | 快速测试、临时验证 | 内存,重启丢失 |
Standalone | 无 | 单实例开发、简单生产 | 本地文件 |
Cluster | ZooKeeper / etcd / Nacos | 多实例部署、生产环境 | 协调中心 |
好,我们来把 mode 彻底展开,逐个模式细讲。
3.2.1 Memory 模式
最简单的模式,没有之一。
mode:
type: Memory
就这一行,没了。
特点:
- 不需要
repository配置。 - 元数据只在内存中,进程重启后所有配置(分片规则等)恢复到
server.yaml的初始状态。 - 状态变化不会持久化,比如你用 DistSQL 在线加了一条分片规则,重启后这条规则就没了。
适用场景:
- 快速跑一个 Demo。
- 单元测试,每次启动都是干净环境。
- 绝不要用于生产。
3.2.2 Standalone 模式
单机生产的标准选择。
mode:
type: Standalone
repository:
type: File
props:
path: .shardingsphere/.meta
max-retries: 3 # 可选,操作失败重试次数
retry-interval: 500 # 可选,重试间隔(毫秒)
repository.type 可选值
| repository.type | 说明 |
|---|---|
File | 存本地文件(推荐) |
Memory | 内存(跟 mode: Memory 一个效果,一般不这么混用) |
File 类型的核心属性:
| 属性 | 默认值 | 说明 |
|---|---|---|
path | 无(必填) | 元数据文件存储目录,相对路径相对于启动目录 |
max-retries | 3 | 写文件失败时的最大重试次数 |
retry-interval | 500ms | 每次重试的间隔时间 |
特点:
- 元数据持久化到本地文件,重启保留。
- 用 DistSQL 在线修改的规则会写入文件,重启后依然生效。
- 不能多实例共享,每个实例各自维护一份元数据文件。
适用场景:
- 单节点部署的生产环境。
- 开发环境,需要重启保留配置。
3.2.3 Cluster 模式
Cluster 模式的本质就是在解决 "规则在线变更,如何让所有实例同步" 的问题。
经典场景
假设你现在的订单表 t_order 有 4 个分片:
ds0.t_order_0, ds0.t_order_1
ds1.t_order_0, ds1.t_order_1
双十一前夕,运维决定在线扩容,加 2 个新库,变成 8 个分片:
原来:ds0, ds1
扩容:ds0, ds1, ds2, ds3
用 Standalone 模式会发生什么
实例A:运维连上去,敲 DistSQL 加了 ds2, ds3 ✅
实例B:完全不知道,数据继续往 ds0, ds1 路由 ❌
实例C:也不知道,查询漏数据 ❌
结果:数据分散不一致,线上事故
用 Cluster 模式
实例A:运维执行 DistSQL 扩容命令
↓
写入 ZK/Nacos
↓
ZK 通知所有订阅者
↓
实例B:收到通知 → 刷新本地元数据 → 新规则生效
实例C:收到通知 → 刷新本地元数据 → 新规则生效
结果:三台实例秒级同步,对外一致服务
用 ZooKeeper
mode:
type: Cluster
repository:
type: ZooKeeper
props:
server-lists: 127.0.0.1:2181 # ZK 地址,多个用逗号分隔
namespace: shardingsphere-demo # 命名空间,跟别的应用隔离
max-retries: 3 # 连接重试次数
retry-interval: 1000 # 重试间隔(毫秒)
time-to-live-seconds: 60 # 临时节点 TTL
operation-timeout-seconds: 30 # 操作超时
核心属性说明:
| 属性 | 必填 | 说明 |
|---|---|---|
server-lists | 是 | ZooKeeper 集群地址 |
namespace | 推荐 | 数据隔离根路径,多个 ShardingSphere 集群可复用同一 ZK |
max-retries | 否 | 连接失败最大重试 |
retry-interval | 否 | 重试间隔 |
用 Nacos
mode:
type: Cluster
repository:
type: Nacos
props:
server-addr: 127.0.0.1:8848 # Nacos 地址
namespace: shardingsphere-demo # Nacos 命名空间 ID
group: SHARDING_SPHERE_DEFAULT_GROUP # 分组名
在 Nacos 控制台,找到对应的命名空间和分组,创建一个配置项:
Data ID: server.yaml(或其他约定名)
Group: SHARDING_SPHERE_DEFAULT_GROUP
配置内容: rules:
- !SHARDING
tables:
t_order:
...
ShardingSphere 启动时会自动从 SHARDING_SPHERE_DEFAULT_GROUP 这个分组下拉取并加载。
Nacos 特殊属性:
| 属性 | 说明 |
|---|---|
server-addr | Nacos 服务地址,逗号分隔多节点 |
namespace | Nacos 的命名空间 ID(不是名字),用于隔离 |
group | 配置分组,默认 SHARDING_SPHERE_DEFAULT_GROUP |
Cluster 模式的运行机制
flowchart TD
A["启动实例1"] --> B["连接协调中心(ZK/etcd/Nacos)"]
B --> C{"是否已有元数据?"}
C -- 无 --> D["写入 server.yaml 规则到协调中心"]
C -- 有 --> E["从协调中心拉取元数据到本地"]
D --> F["注册为 WORKER"]
E --> F
G["启动实例2"] --> B
F --> H["开始对外服务"]
I["DistSQL 修改规则"] --> J["更新协调中心元数据"]
J --> K["通知所有实例同步变更"]
关键特性:
- 配置中心化:所有实例从协调中心拉取同一份配置。
- 自动同步:任一实例通过 DistSQL 修改规则,其他实例会收到通知并实时更新。
- 主节点选举:集群会自动选出一个主节点,负责执行一些全局性任务(如数据迁移协调)。
- 高可用:单个实例挂掉不影响整体服务。
适用场景:
- 所有多实例部署的生产环境。
- 需要在线动态变更分片规则的场景。
- 元数据需要集中管理和备份的场景。
3.2.4 各种模式对比总结
| 维度 | Memory | Standalone (File) | Cluster |
|---|---|---|---|
| 重启保留配置 | ❌ | ✅ | ✅ |
| 多实例共享配置 | ❌ | ❌ | ✅ |
| 在线变更规则同步 | ❌ | 仅本地 | 全集群秒级同步 |
| 外部依赖 | 无 | 无 | ZK / etcd / Nacos |
| 生产就绪 | ❌ | 单节点可 | ✅ |
| 入门难度 | 极低 | 低 | 中 |
3.3 dataSources(数据源定义)
定义所有真实数据库的连接信息,后续分片规则会引用这里的名字。
dataSources:
自定义数据源名:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource # 连接池类
driverClassName: com.mysql.cj.jdbc.Driver # 驱动类
jdbcUrl: jdbc:mysql://127.0.0.1:3306/db_0?... # 连接串
username: root # 用户名
password: root # 密码
maxPoolSize: 20 # 可选:连接池参数
minIdle: 5 # 可选:连接池参数
讲解:
- 每个数据源的名字(
ds0、ds1)是逻辑名,在分片规则的actualDataNodes中会用到。 dataSourceClassName指定连接池实现,HikariCP 是目前最常用的。- 连接参数直接在下方平铺即可,会被透传给连接池。
3.3.1 Hikari连接池
ds0:
# ... 基础连接信息 ...
dataSourceClassName: com.zaxxer.hikari.HikariDataSource # 连接池类
poolName: HikariPool-ds0 # 连接池名称,日志排查用
leakDetectionThreshold: 60000 # 连接泄漏检测,60秒未归还就告警
keepaliveTime: 30000 # 保活检测间隔,防止被数据库踢掉
validationTimeout: 5000 # 连接校验超时
3.3.2 Druid连接池
ds0:
dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
# ... 基础连接信息 ...
# ===== Druid 独有 =====
# 监控统计
filters: stat,wall # stat=SQL监控, wall=防SQL注入
maxActive: 20 # 最大连接数(Druid 特有命名)
initialSize: 5 # 启动时初始连接数
minIdle: 5
maxWait: 30000 # 获取连接超时(Druid 特有命名)
# 检测与回收
testWhileIdle: true # 空闲时检测连接是否有效
testOnBorrow: false # 借出时检测(性能影响大,建议false)
timeBetweenEvictionRunsMillis: 60000 # 检测线程运行间隔
# 监控页面
stat-view-servlet: # 这个是特殊配置,Druid Web监控页
enabled: true
url-pattern: /druid/*
3.4 rules(规则定义)
定义对数据的所有操作规则:怎么分片、怎么读写分离、怎么加密、怎么脱敏。
- 一个 rules 下可以挂多种规则,每种用 ! 开头声明类型,是数组,可以写多个:
rules:
- !SHARDING # 分库分表规则
- !READWRITE_SPLITTING # 读写分离规则
- !ENCRYPT # 数据加密规则
- !MASK # 数据脱敏规则
- !SHADOW # 影子库压测规则
3.4.1 !SHARDING
rules:
- !SHARDING
tables: # 要分片的表,每个表单独配置
逻辑表名:
actualDataNodes: ... # 真实节点映射
databaseStrategy: ... # 分库策略
tableStrategy: ... # 分表策略
bindingTables: # 绑定表,避免关联查询笛卡尔积
broadcastTables: # 广播表,每个库全量同步
defaultDatabaseStrategy: # 默认分库策略,没单独配的表走这个
defaultTableStrategy: # 默认分表策略
shardingAlgorithms: # 分片算法定义,上面策略引用的
3.4.2 !READWRITE_SPLITTING
rules:
- !READWRITE_SPLITTING
dataSources:
逻辑读写名:
writeDataSourceName: ds_master # 写数据源名
readDataSourceNames: # 读数据源名列表
- ds_slave_0
- ds_slave_1
loadBalancerName: round_robin # 读库负载均衡算法
loadBalancers: # 负载均衡算法定义
round_robin:
type: ROUND_ROBIN
3.4.3 !ENCRYPT — 数据加密
rules:
- !ENCRYPT
tables:
t_user:
columns:
身份证号:
cipherColumn: id_card_cipher # 密文字段
encryptorName: aes_encryptor # 加密算法名
encryptors:
aes_encryptor:
type: AES
props:
aes-key-value: 123456abc
不用改代码,配置一下自动加解密。
3.4.4 !MASK — 数据脱敏
rules:
- !MASK
tables:
t_user:
columns:
手机号:
maskColumn: phone_mask # 脱敏后字段
maskAlgorithmName: phone_masker
maskAlgorithms:
phone_masker:
type: MASK_FIRST_N_LAST_M
props:
first-n: 3 # 保留前3位
last-m: 4 # 保留后4位
查出来 13812345678 自动变 138****5678。
3.4.5 !SHADOW — 影子库压测
rules:
- !SHADOW
dataSources:
shadow-data-source: # 影子库的读写分离逻辑名
productionDataSourceName: 生产逻辑名
shadowDataSourceName: 影子逻辑名
tables:
t_order:
shadowAlgorithmNames:
- user_id_shadow_test
shadowAlgorithms:
user_id_shadow_test:
type: VALUE_MATCH
props:
column: user_id
operation: insert
value: shadow_user_001 # 只有这个用户在影子库操作
压测流量和真实流量自动隔离。
3.4.6 总结
| 规则 | 解决什么问题 |
|---|---|
!SHARDING | 分库分表 |
!READWRITE_SPLITTING | 读写分离 |
!ENCRYPT | 数据加解密 |
!MASK | 数据脱敏 |
!SHADOW | 影子库压测 |
3.5 配置分库分表规则
3.5.1 !SHARDING 内部六大块
rules:
- !SHARDING
tables: # 1. 分片表配置(核心)
bindingTables: # 2. 绑定表
broadcastTables: # 3. 广播表
defaultDatabaseStrategy: # 4. 默认分库策略
defaultTableStrategy: # 5. 默认分表策略
shardingAlgorithms: # 6. 分片算法定义
3.5.2 tables — 分片表配置
干什么:指定哪些表要分片,数据存在哪些真实节点上。
能配什么:
tables:
逻辑表名: # 代码里写的表名
actualDataNodes: ds$->{0..1}.表_$->{0..1} # 真实节点表达式
databaseStrategy: # 分库策略
tableStrategy: # 分表策略
关键:actualDataNodes 表达式
| 表达式 | 展开结果 |
|---|---|
ds$->{0..1}.t_order_$->{0..1} | ds0.t_order_0, ds0.t_order_1, ds1.t_order_0, ds1.t_order_1 |
ds$->{0..2}.t_order | ds0.t_order, ds1.t_order, ds2.t_order(只分库不分表) |
ds0.t_order_$->{0..3} | ds0.t_order_0 到 t_order_3(只分表不分库) |
3.5.3 bindingTables — 绑定表
干什么:关联查询的多张表按同样分片键路由到同一个节点,避免笛卡尔积。
背景介绍
没配绑定表前
t_order按order_id分片,t_order_item也按order_id分片。执行查询:
SELECT * FROM t_order o JOIN t_order_item i ON o.order_id = i.order_id WHERE o.order_id = 1001路由过程:
t_order 的 order_id=1001 → 路由到 ds1.t_order_1 ✅ t_order_item 的 order_id=1001 → 也会路由到 ds1.t_order_item_1但因为没配绑定表,ShardingSphere 不敢确定这两张表的分片逻辑一致,所以它为了安全会生成笛卡尔积:
ds0.t_order_0 JOIN ds0.t_order_item_0 ds0.t_order_0 JOIN ds0.t_order_item_1 ds0.t_order_0 JOIN ds1.t_order_item_0 ds0.t_order_0 JOIN ds1.t_order_item_1 ds0.t_order_1 JOIN ds0.t_order_item_0 ...(16种组合全试一遍)然后在所有结果里筛出
order_id = 1001的数据,性能直接崩。
配了绑定表后
bindingTables: - t_order, t_order_item告诉 ShardingSphere:这两张表分片逻辑完全一样,同一个
order_id一定落在同一个节点。路由变成:
order_id=1001 │ ▼ 路由计算:user_id % 2 = 1 → ds1 order_id % 2 = 1 → t_order_1, t_order_item_1 │ ▼ ds1.t_order_1 JOIN ds1.t_order_item_1 一条SQL,直接命中总结
没配绑定表 配了绑定表 SQL 生成 笛卡尔积,N×N 条 1 条精确路由 性能 崩 正常 前提 — 两张表分片键和分片算法必须一致
能配什么:
bindingTables:
- t_order, t_order_item
- t_user, t_user_ext
一行一组,逗号分隔。t_order 和 t_order_item 都会按相同逻辑路由。
3.5.4 broadcastTables — 广播表
干什么:数据量小、每个库都需要的全量表。
能配什么:
broadcastTables:
- t_config
- t_dict
写操作广播到所有节点,读操作随机挑一个节点。
3.5.5 默认分库/分表策略
干什么:没在 tables 里单独配策略的表,走这个默认策略。
能配什么:
defaultDatabaseStrategy:
standard:
shardingColumn: user_id # 分片字段
shardingAlgorithmName: default_db_algo # 分片算法
defaultTableStrategy:
none # 不分表
策略类型和单表里完全一样,只是一个公共默认值。
3.5.6 shardingAlgorithms — 分片算法定义
干什么:定义具体的分片算法逻辑,给上面的策略引用。
能配的类型:
shardingAlgorithms:
算法名:
type: INLINE | MOD | HASH_MOD | RANGE | INTERVAL | ...
props:
# 具体参数,每个 type 不同
能配什么类型:
| type | 怎么分 |
|---|---|
INLINE | 写 Groovy 表达式,最灵活 |
MOD | 直接取模 |
HASH_MOD | 先哈希再取模,分布更均匀 |
RANGE | 按范围分,比如用户ID 1-1000 进一个库 |
INTERVAL | 按日期自动分,比如按月分表 |
-
INLINE — 行表达式
最常用的方式,直接在
algorithm-expression里写 Groovy 表达式。shardingAlgorithms: db_algo: type: INLINE props: algorithm-expression: ds$->{user_id % 2}表达式里能用的变量:你在策略里配的
shardingColumn字段值会自动注入。# 分库策略配了 shardingColumn: user_id algorithm-expression: ds$->{user_id % 2} # ↑ user_id 就是传入的分片键值 # 分表策略配了 shardingColumn: order_id algorithm-expression: t_order_$->{order_id % 4} # ↑ order_id 就是传入的分片键值支持的操作符:
操作 示例 含义 %user_id % 2取模 ..$->{0..1}范围枚举 三元 value > 1000 ? 1 : 0条件判断 -
MOD — 取模分片
就是纯粹的取模,和 INLINE 写
%效果一样,只是换个写法:shardingAlgorithms: table_algo: type: MOD props: sharding-count: 4 # 分片数量分片键值直接对
sharding-count取模,结果 0、1、2、3 分别对应四张表。 -
HASH_MOD — 哈希取模
和 MOD 的区别:先对分片键做一次哈希,再取模。
shardingAlgorithms: table_algo: type: HASH_MOD props: sharding-count: 4为什么要哈希一下:分片键是连续自增的,用 HASH_MOD 避免数据倾斜。
场景 直接用 MOD 用 HASH_MOD 分片键是自增 ID(1,2,3,4,5...) 连续数据进连续分片,热点不均 哈希打散,均匀分布 分片键本身均匀(如 UUID) 已经均匀,没必要 效果一样 -
RANGE — 范围分片
按值的范围区间路由。
shardingAlgorithms: range_algo: type: RANGE props: sharding-ranges: | 0..1000=0, 1001..2000=1, 2001..3000=2分片键值落在哪个区间,就路由到对应编号。
适用场景:按用户等级、按地区编号等有明确区间的字段。
-
INTERVAL — 日期分片
按日期自动分表,比如按月。
shardingAlgorithms:
time_algo:
type: INTERVAL
props:
datetime-pattern: yyyy-MM-dd HH:mm:ss # 日期格式
sharding-suffix-pattern: yyyyMM # 分片后缀格式
datetime-interval-amount: 1 # 间隔数量
datetime-interval-unit: MONTHS # 间隔单位
示例:
分片键是 create_time = '2025-03-15 12:00:00'
datetime-suffix-pattern: yyyyMM → 202503
路由到表: t_order_202503
适用场景:日志表、流水表等按时间自然增长的表。
3.5.7 分片策略
分片策略就是告诉 ShardingSphere “用什么字段、走哪个算法” 来路由数据
四种策略类型
databaseStrategy: # 或 tableStrategy
standard: # 1. 标准策略
complex: # 2. 复合策略
hint: # 3. 强制路由
none: # 4. 不分片
-
standard — 标准策略
单个分片键,支持
=、IN、BETWEEN操作。databaseStrategy: standard: shardingColumn: user_id # 分片键 shardingAlgorithmName: db_algo # 引用算法名就是配一个字段 + 一个算法,最简单的组合。
适用的 SQL:
SELECT * FROM t_order WHERE user_id = 1001 -- ✅ SELECT * FROM t_order WHERE user_id IN (1,2,3) -- ✅ SELECT * FROM t_order WHERE order_id = 2002 -- ❌ 不是分片键,会全路由 -
complex — 复合策略
多个分片键同时参与路由。比如同时按
user_id和order_id分片。databaseStrategy: complex: shardingColumns: user_id, order_id # 多个分片键 shardingAlgorithmName: complex_algo对应的算法也要自定义,INLINE 表达式不够用了:
shardingAlgorithms: complex_algo: type: CLASS_BASED # 自定义算法类 props: strategy: COMPLEX algorithmClassName: com.xxx.MyComplexAlgorithm需要写代码实现
ComplexKeysShardingAlgorithm接口。不常用,一般分片键设一个就够了。 -
hint — 强制路由
databaseStrategy: hint: shardingAlgorithmName: hint_algo代码里这样写:
// 强制告诉 ShardingSphere:这条 SQL 走 ds1 HintManager hintManager = HintManager.getInstance(); hintManager.setDatabaseShardingValue("ds1"); // 执行 SQL ... hintManager.close();适用场景:管理员后台要查所有分片,或临时紧急指定路由。
-
none — 不分片
tableStrategy: none: # 这张表不分表,只分库什么时候用:一张表只分库不分表,或者只分表不分库,没用到的那层就写
none。
databaseStrategy 和 tableStrategy 都用 standard 的完整示例
tables:
t_order:
actualDataNodes: ds$->{0..1}.t_order_$->{0..1}
databaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: db_algo
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: table_algo
分库看 user_id,分表看 order_id,各走各的算法。
3.5.8 六大块关系总结
shardingAlgorithms(定义算法)
│
│ 被引用
▼
tables.databaseStrategy(每张表的分库策略)
tables.tableStrategy (每张表的分表策略)
defaultDatabaseStrategy (兜底分库)
defaultTableStrategy (兜底分表)
│
│ 路由到
▼
tables.actualDataNodes (真实节点)
│
│ 关联优化
▼
bindingTables(绑定表)
broadcastTables(广播表)
3.6 配置读写分离规则
干什么:写操作自动走主库,读操作自动走从库。
内部结构:
rules:
- !READWRITE_SPLITTING
dataSources: # 1. 定义读写分离数据源
loadBalancers: # 2. 定义从库负载均衡算法
3.6.1 dataSources — 定义读写分离数据源
干什么:把物理主库和从库绑成一个逻辑数据源。
- !READWRITE_SPLITTING
dataSources:
逻辑数据源名: # 代码里用的名字
writeDataSourceName: ds_master # 引用 dataSources 里的主库名
readDataSourceNames: # 引用 dataSources 里的从库名
- ds_slave_0
- ds_slave_1
loadBalancerName: round_robin # 引用下面定义的负载均衡算法
配置关系:
dataSources 里定义物理库:
ds_master → 192.168.1.10 (主)
ds_slave_0 → 192.168.1.11 (从)
ds_slave_1 → 192.168.1.12 (从)
读写分离规则绑定:
readwrite_ds
├── 写 → ds_master
└── 读 → ds_slave_0 或 ds_slave_1 (轮询)
3.6.2 loadBalancers — 从库负载均衡算法
干什么:多个从库时,读请求选哪一个。
loadBalancers:
算法名:
type: ROUND_ROBIN # 轮询
# 或
type: RANDOM # 随机
# 或
type: WEIGHT # 权重
props:
ds_slave_0: 2 # 权重值
ds_slave_1: 1
三种算法:
| type | 逻辑 | 用在哪 |
|---|---|---|
ROUND_ROBIN | 挨个轮询 | 从库配置一样 |
RANDOM | 随机挑 | 从库配置一样 |
WEIGHT | 按权重分配 | 从库配置不一样,好的机器多分流量 |
3.6.3 完整示例
rules:
- !READWRITE_SPLITTING
dataSources:
readwrite_ds:
writeDataSourceName: ds_master
readDataSourceNames:
- ds_slave_0
- ds_slave_1
loadBalancerName: round_robin
loadBalancers:
round_robin:
type: ROUND_ROBIN
3.6.4 和分库分表组合
读写分离数据源名可以直接当分片节点用:
rules:
- !READWRITE_SPLITTING
dataSources:
readwrite_ds0: # 第0个分片的读写分离组
writeDataSourceName: ds0_master
readDataSourceNames:
- ds0_slave_0
readwrite_ds1: # 第1个分片的读写分离组
writeDataSourceName: ds1_master
readDataSourceNames:
- ds1_slave_0
- !SHARDING
tables:
t_order:
actualDataNodes: readwrite_ds$->{0..1}.t_order_$->{0..1}
# ↑ 这里用的是读写分离逻辑名,不是物理库名
路由过程:
一条写SQL
→ 先分片:user_id=1001 → readwrite_ds1
→ 再读写分离:写操作 → ds1_master
→ 最终落到 ds1_master.t_order_1
3.7 配置数据加密规则
干什么:存数据自动加密,读数据自动解密,代码不用改一行。
内部结构
rules:
- !ENCRYPT
tables: # 1. 要加密的表和字段
encryptors: # 2. 加密算法定义
3.7.1 tables — 配置要加密的表和字段
tables:
表名:
columns:
逻辑列名: # 代码里写的字段名
cipherColumn: 密文字段 # 数据库里存密文的字段
plainColumn: 明文字段 # 可选,存明文的字段(查询用)
encryptorName: 加密算法名 # 引用下面的算法
一个实际例子:
tables:
t_user:
columns:
pwd: # 代码里叫 pwd
cipherColumn: pwd_cipher # 数据库密文列
plainColumn: pwd_plain # 数据库明文列(可选)
encryptorName: aes_encryptor # 用 aes 加密
数据流转:
插入数据:代码写 pwd='123456'
→ 加密 → pwd_cipher='xY7Kd...'
→ 明文列(可选) → pwd_plain='123456'
查询数据:SELECT pwd FROM t_user
→ 读到 pwd_cipher='xY7Kd...'
→ 解密 → 返回 pwd='123456'
3.7.2 encryptors — 加密算法定义
encryptors:
算法名:
type: AES # 或 MD5、SM4 等
props:
aes-key-value: 123456abc # AES 密钥
常用算法 type:
| type | 说明 | 需要参数 |
|---|---|---|
AES | 对称加密,可逆 | aes-key-value(密钥) |
SM4 | 国密加密,可逆 | sm4-key |
MD5 | 不可逆哈希,验密用 | 无 |
3.7.3 完整示例
rules:
- !ENCRYPT
tables:
t_user:
columns:
pwd:
cipherColumn: pwd_cipher
encryptorName: aes_encryptor
phone:
cipherColumn: phone_cipher
encryptorName: aes_encryptor
encryptors:
aes_encryptor:
type: AES
props:
aes-key-value: 123456abc
效果:
| 操作 | 代码 | 数据库实际 |
|---|---|---|
| 插入 | pwd='123456' | pwd_cipher='加密串' |
| 查询 | SELECT pwd | 自动返回 '123456' |
| WHERE | WHERE pwd='123456' | 自动转 WHERE pwd_cipher='加密串' |
全程透明,业务代码零感知。
3.7.4 plainColumn 什么时候用
加了 plainColumn,同时存明文和密文:
pwd:
cipherColumn: pwd_cipher
plainColumn: pwd_plain # 加这个
encryptorName: aes_encryptor
好处:WHERE 条件走明文索引,查询更快。
坏处:明文也存了,安全性降低。
3.8 配置数据脱敏规则
干什么:查出来的敏感数据自动脱敏,代码不用改。
内部结构
rules:
- !MASK
tables: # 1. 要脱敏的表和字段
maskAlgorithms: # 2. 脱敏算法定义
3.8.1 tables — 配置要脱敏的表和字段
tables:
表名:
columns:
逻辑列名: # 代码里写的字段名
maskColumn: 脱敏字段 # 脱敏后的数据存哪(通常就是自己)
maskAlgorithmName: 算法名 # 引用下面的算法
一个实际例子:
tables:
t_user:
columns:
phone:
maskColumn: phone # 脱敏后还叫 phone
maskAlgorithmName: phone_masker
效果:
数据库存:phone = '13812345678'
查询返回:phone = '138****5678' ← 自动脱敏
3.8.2 maskAlgorithms — 脱敏算法定义
常用算法:
maskAlgorithms:
# ===== 手机号:保留前3后4 =====
phone_masker:
type: MASK_FIRST_N_LAST_M
props:
first-n: 3 # 保留前3位
last-m: 4 # 保留后4位
replace-char: '*' # 中间替换成什么
# ===== 身份证:保留后4位 =====
id_card_masker:
type: MASK_FROM_X_TO_Y
props:
from-x: 1 # 从第1位开始脱
to-y: 14 # 脱到第14位
replace-char: '*'
# ===== 姓名:只留姓 =====
name_masker:
type: MASK_FIRST_N_LAST_M
props:
first-n: 1
last-m: 0
replace-char: '*'
# ===== 邮箱:只留@前面第一个字和@后面 =====
email_masker:
type: MASK_EMAIL
props:
replace-char: '*'
3.8.3 常用算法类型速查
| type | 逻辑 | 示例效果 |
|---|---|---|
MASK_FIRST_N_LAST_M | 保留前N后M,中间替换 | 138****5678 |
MASK_FROM_X_TO_Y | 替换第X到第Y位 | 1***2345678 |
MASK_EMAIL | 邮箱专用 | t***@qq.com |
MASK_ALL | 全替换 | **** |
3.8.4 完整示例
rules:
- !MASK
tables:
t_user:
columns:
phone:
maskColumn: phone
maskAlgorithmName: phone_masker
id_card:
maskColumn: id_card
maskAlgorithmName: id_card_masker
maskAlgorithms:
phone_masker:
type: MASK_FIRST_N_LAST_M
props:
first-n: 3
last-m: 4
replace-char: '*'
id_card_masker:
type: MASK_FROM_X_TO_Y
props:
from-x: 1
to-y: 14
replace-char: '*'
查询效果
| 数据库存 | 脱敏后返回 |
|---|---|
13812345678 | 138****5678 |
320102199001011234 | **************1234 |
3.9 配置影子库压测规则
3.9.1 背景介绍
什么是影子库压测
- 把压测流量和真实流量隔离开,压测的数据写到假的"影子库"里,不影响真实数据。
- 核心思想:压测流量和真实流量,同一个系统,两套数据,互不影响。
为什么要这样
你线上有个订单库 order_db,里面是真实用户数据。
现在想看系统能扛多大并发,直接往这个库压测:
压测流量 → order_db
真实流量 → order_db
↑
混在一起,压测的假数据也写进去了,灾难
影子库的做法:
压测流量 → 影子库 shadow_db(假库,随便搞)
真实流量 → 生产库 order_db(真库,不受影响)
↑
完全隔离
怎么判断哪个流量是压测的
通常两种方式:
-
方式一:SQL 里带特殊字段值
-- 正常用户 INSERT INTO t_order(user_id, price) VALUES('normal_user', 100) -- 压测用户(user_id 有特殊前缀) INSERT INTO t_order(user_id, price) VALUES('shadow_user_001', 100) -
方式二:SQL Hint 注释
-- 正常 SQL SELECT * FROM t_order WHERE order_id=1001 -- 压测 SQL /* shadow:true */ SELECT * FROM t_order WHERE order_id=1001ShardingSphere 根据这些特征判断:这是压测流量,路由到影子库。
3.9.2 !SHADOW 影子库配置
内部结构:
rules:
- !SHADOW
dataSources: # 1. 影子库数据源映射
tables: # 2. 哪些表要走影子库
shadowAlgorithms: # 3. 判断"是不是压测流量"的算法
-
dataSources — 影子库映射
dataSources: 影子逻辑名: productionDataSourceName: 生产库逻辑名 # 真实库 shadowDataSourceName: 影子库逻辑名 # 压测库 -
tables — 哪些表参与影子压测
tables:
t_order:
shadowAlgorithmNames: # 用哪个算法判断压测流量
- user_id_shadow_test
-
shadowAlgorithms — 判断压测流量的算法
shadowAlgorithms: user_id_shadow_test: type: VALUE_MATCH # 值匹配算法 props: column: user_id # 判断哪个字段 operation: insert # 哪种操作要走影子库 value: shadow_user_001 # 匹配的值效果
-- user_id 是 shadow_user_001 → 压测流量 → 走影子库 INSERT INTO t_order(user_id) VALUES('shadow_user_001') -- user_id 不是 → 正常流量 → 走生产库 INSERT INTO t_order(user_id) VALUES('real_user')
3.9.3 完整示例
rules:
- !SHADOW
dataSources:
shadow_ds:
productionDataSourceName: readwrite_ds
shadowDataSourceName: shadow_readwrite_ds
tables:
t_order:
shadowAlgorithmNames:
- user_id_shadow_test
shadowAlgorithms:
user_id_shadow_test:
type: VALUE_MATCH
props:
column: user_id
operation: insert
value: shadow_user_001
3.10 props全局属性
干什么:控制 ShardingSphere 的全局行为和开关。
能配什么
props:
sql-show: true # 打印最终路由的 SQL
sql-simple: false # 简化日志,不打印参数占位符
max-connections-size-per-query: 1 # 单次查询每个库最大连接数
check-table-metadata-enabled: true # 启动时检查表元数据
sql-federation-enabled: false # 跨库关联查询优化(实验性)
3.10.1 sql-show
props:
sql-show: true
效果:每次执行 SQL,控制台打印路由后的真实 SQL。
逻辑 SQL:SELECT * FROM t_order WHERE user_id=1001
真实 SQL:ds0: SELECT * FROM t_order_1 WHERE user_id=1001
ds1: SELECT * FROM t_order_1 WHERE user_id=1001
调试必备,生产环境建议关掉。
3.10.2 sql-simple
props:
sql-simple: true
效果:日志里不显示具体参数值,保护敏感数据。
sql-show=true, sql-simple=false:WHERE user_id=1001 ← 显示值
sql-show=true, sql-simple=true: WHERE user_id=? ← 只显示占位符
3.10.3 max-connections-size-per-query
props:
max-connections-size-per-query: 1
控制单条 SQL 最多同时占用多少个数据库连接。
一个查询要跨 4 个分片,设 1 则一个库一个库串行查,设 4 则 4 个库并行查。
3.10.4 check-table-metadata-enabled
props:
check-table-metadata-enabled: true
启动时检查配置的表是否存在,不存在直接报错,防止上线后发现表没建。
3.10.5 总结
| 属性 | 默认值 | 建议 |
|---|---|---|
sql-show | false | 开发打开,生产关闭 |
sql-simple | false | 生产建议 true |
max-connections-size-per-query | 1 | 看分片数,设大些可并行查 |
check-table-metadata-enabled | true | 保持 true |
ShardingSphere详解
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。