一,ShardingSphere 介绍

Apache ShardingSphere 是一个开源的分布式数据库生态系统,简单来说,它处于你的应用和数据库之间,通过构建一层代理或增强,帮你把复杂的数据库操作变得简单高效。

它能解决什么问题?

随着业务发展,数据量激增,一个数据库往往难以应付。ShardingSphere 主要通过以下方式解决这个难题:

  • 应对数据暴涨:当一张表的数据量达到千万级甚至亿级时,查询会变慢。ShardingSphere 可以将一张表的数据水平拆分,分散存储到多个数据库或表中,突破单机的性能瓶颈。
  • 突破并发瓶颈:当数据库每秒处理的请求数(QPS)过高时,它可以将请求分散到多个数据库实例上,成倍提升系统的吞吐能力。
  • 提升系统可用性:通过分库,将数据分散部署,单一节点故障只影响部分数据,避免了"一个人感冒,全公司请假"的局面,提高了系统整体的可用性。

ShardingSphere 的目标就是让你像操作一个数据库一样,轻松管理成百上千个数据库实例。

核心特性一览

ShardingSphere 功能丰富,远不止分库分表,官网列出了以下核心能力:

  • 数据分片:其看家本领,通过灵活的分片策略,透明化地实现分库分表,解决了海量数据存储和高并发访问的瓶颈。
  • 读写分离:自动将"写"操作指向主库,"读"操作负载均衡地分发到从库,有效提升系统的整体吞吐量。
  • 分布式事务:提供了 XA 强一致事务和 BASE 柔性事务两种模式,让你在数据分散后,依然能保证跨库操作的数据一致性。
  • 数据加密:支持对数据库中的敏感字段(如身份证、手机号)进行透明的加密存储,业务代码无需改动即可实现数据安全防护。
  • SQL 联邦查询:支持跨多个数据源进行复杂的联合查询,简化数据聚合的难度。
  • 云原生与多数据库支持:完美适配 MySQL、PostgreSQL、openGauss 等多种主流数据库,并能无缝集成到云原生环境中。

产品形态

ShardingSphere 不是一个单一产品,而是由三部分组成,你可以根据场景灵活选择:

产品形态工作方式一句话总结适用场景
Sharding-JDBCJAR包 形式嵌入你的 Java 应用,在客户端进行SQL路由和改写。Java 开发者首选,轻量级,性能最高,没有额外的中间件部署开销。
Sharding-Proxy一个独立的 代理服务,对应用透明。应用像连接普通数据库一样连接它即可。适合异构语言,或希望 DBA 统一进行运维管理的场景,对业务代码零侵入。
Sharding-SidecarSidecar 容器 形式运行,是云原生架构下的数据面。面向未来的 云原生架构,适用于 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 决定三件事:

  1. 元数据存哪里(分片规则、数据源配置等)
  2. 是否需要集群协调(多实例间配置同步、主节点选举)
  3. 配置如何下发

先看一个总览表:

mode.type依赖组件适用场景元数据存储
Memory快速测试、临时验证内存,重启丢失
Standalone单实例开发、简单生产本地文件
ClusterZooKeeper / 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-retries3写文件失败时的最大重试次数
retry-interval500ms每次重试的间隔时间

特点:

  • 元数据持久化到本地文件,重启保留。
  • 用 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-listsZooKeeper 集群地址
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-addrNacos 服务地址,逗号分隔多节点
namespaceNacos 的命名空间 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["通知所有实例同步变更"]

关键特性:

  1. 配置中心化:所有实例从协调中心拉取同一份配置。
  2. 自动同步:任一实例通过 DistSQL 修改规则,其他实例会收到通知并实时更新。
  3. 主节点选举:集群会自动选出一个主节点,负责执行一些全局性任务(如数据迁移协调)。
  4. 高可用:单个实例挂掉不影响整体服务。

适用场景:

  • 所有多实例部署的生产环境
  • 需要在线动态变更分片规则的场景。
  • 元数据需要集中管理和备份的场景。

3.2.4 各种模式对比总结

维度MemoryStandalone (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                                                # 可选:连接池参数

讲解:

  • 每个数据源的名字(ds0ds1)是逻辑名,在分片规则的 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_orderds0.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_orderorder_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_ordert_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按日期自动分,比如按月分表
  1. 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条件判断
  2. MOD — 取模分片

    就是纯粹的取模,和 INLINE 写 % 效果一样,只是换个写法:

    shardingAlgorithms:
      table_algo:
        type: MOD
        props:
          sharding-count: 4    # 分片数量
    

    分片键值直接对 sharding-count 取模,结果 0、1、2、3 分别对应四张表。

  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)已经均匀,没必要效果一样
  4. RANGE — 范围分片

    按值的范围区间路由

    shardingAlgorithms:
      range_algo:
        type: RANGE
        props:
          sharding-ranges: |
            0..1000=0,
            1001..2000=1,
            2001..3000=2
    

    分片键值落在哪个区间,就路由到对应编号。

    适用场景:按用户等级、按地区编号等有明确区间的字段。

  5. 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. 不分片
  1. standard — 标准策略

    单个分片键,支持 =INBETWEEN 操作。

    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     -- ❌ 不是分片键,会全路由
    
  2. complex — 复合策略

    多个分片键同时参与路由。比如同时按 user_idorder_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 接口。不常用,一般分片键设一个就够了。

  3. hint — 强制路由

    databaseStrategy:
      hint:
        shardingAlgorithmName: hint_algo
    

    代码里这样写

    // 强制告诉 ShardingSphere:这条 SQL 走 ds1
    HintManager hintManager = HintManager.getInstance();
    hintManager.setDatabaseShardingValue("ds1");
    // 执行 SQL ...
    hintManager.close();
    

    适用场景:管理员后台要查所有分片,或临时紧急指定路由。

  4. 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'
WHEREWHERE 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: '*'

查询效果

数据库存脱敏后返回
13812345678138****5678
320102199001011234**************1234

3.9 配置影子库压测规则

3.9.1 背景介绍

什么是影子库压测

  • 把压测流量和真实流量隔离开,压测的数据写到假的"影子库"里,不影响真实数据。
  • 核心思想:压测流量和真实流量,同一个系统,两套数据,互不影响。

为什么要这样

你线上有个订单库 order_db,里面是真实用户数据。

现在想看系统能扛多大并发,直接往这个库压测:

压测流量 → order_db
真实流量 → order_db
         ↑
      混在一起,压测的假数据也写进去了,灾难

影子库的做法

压测流量 → 影子库 shadow_db(假库,随便搞)
真实流量 → 生产库 order_db(真库,不受影响)
         ↑
      完全隔离

怎么判断哪个流量是压测的

通常两种方式:

  1. 方式一: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)
    
  2. 方式二:SQL Hint 注释

    -- 正常 SQL
    SELECT * FROM t_order WHERE order_id=1001
    
    -- 压测 SQL
    /* shadow:true */ SELECT * FROM t_order WHERE order_id=1001
    

    ShardingSphere 根据这些特征判断:这是压测流量,路由到影子库。

3.9.2 !SHADOW 影子库配置

内部结构

rules:
  - !SHADOW
    dataSources:           # 1. 影子库数据源映射
    tables:                # 2. 哪些表要走影子库
    shadowAlgorithms:      # 3. 判断"是不是压测流量"的算法
  1. dataSources — 影子库映射

        dataSources:
          影子逻辑名:
            productionDataSourceName: 生产库逻辑名    # 真实库
            shadowDataSourceName: 影子库逻辑名        # 压测库
    
  2. tables — 哪些表参与影子压测

    tables:
      t_order:
        shadowAlgorithmNames:           # 用哪个算法判断压测流量
          - user_id_shadow_test
  1. 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-showfalse开发打开,生产关闭
sql-simplefalse生产建议 true
max-connections-size-per-query1看分片数,设大些可并行查
check-table-metadata-enabledtrue保持 true