# 基础配置

作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
代码参考:[https://github.com/YangYingmeng/learning_shardingJDBC)


# 一、主键策略对比

在分库分表场景下,主键的生成策略将直接影响系统的性能、扩展性以及故障恢复能力。常见方案主要包括以下四类:

# 数据库自增 ID

通过为不同数据库设置不同的 自增起始值 + 步长,避免跨库主键冲突:

DB1:单数 → 从 1 开始,每次 +2
DB2:偶数 → 从 2 开始,每次 +2
1
2

对应 MySQL 参数:

  • auto_increment_offset
  • auto_increment_increment

优点:

  • 实现简单
  • 有序递增,利于索引性能

缺点:

  • 严重依赖数据库,扩容成本高
  • 主从切换可能导致 发号不一致
  • 高并发下存在 单点性能瓶颈
  • 分库数量变化时,步长策略维护复杂

适用于:小规模、非核心分布式系统

# UUID

直接由程序生成:

优点:

  • 本地生成,无网络消耗
  • 理论上全局唯一
  • 性能极高

缺点:

  • 无序,不具备趋势递增特性
  • UUID 字符串过长,占用存储空间大
  • 对数据库索引极不友好(B+Tree 频繁随机写)
  • 不适合订单、流水号等业务主键

适用于:日志系统、非核心业务唯一标识

# Redis 发号器(INCR / INCRBY)

利用 Redis 的原子自增能力:

DB1:从 1 开始,每次 +2  
DB2:从 2 开始,每次 +2  
1
2

优点:

  • 原子性强,线程安全
  • 性能远高于 MySQL
  • 可保证全局唯一

缺点:

  • 依赖 Redis,存在网络开销
  • 系统架构复杂度增加
  • Redis 高可用、持久化都需要额外维护

适用于:中等规模分布式系统

# Snowflake 雪花算法

由 Twitter 开源的分布式 ID 生成算法,特点是:

优点:

  • 本地生成,不依赖数据库和网络
  • 高性能,高并发场景稳定
  • ID 基于时间戳,基本保证趋势递增
  • 数据迁移、分库分表不受影响

缺点:

  • 依赖系统时钟(必须防止时间回拨)
  • 分布式部署时必须保证 workerId 唯一

# 二、Snowflake 雪花算法原理简析

# Snowflake 是什么?

Snowflake 是 Twitter 使用 Scala 实现的一种 分布式唯一 ID 生成算法,核心特点:

  • 生成的 ID 全局唯一
  • 性能极高
  • 基于时间戳,基本有序递增

Snowflake 生成的是一个 long 类型数字:

  • 8 字节(64 bit)
  • 有符号范围:
-2^63  ~  2^63 - 1
1

由于数据库主键通常不使用负数,因此实际可用范围是:

0 ~ 2^63 - 1
≈ 9.22 × 10^18
1
2

其经典结构如下:

0 | 41bit 时间戳 | 10bit 机器ID | 12bit 序列号
1

# Snowflake 两大经典踩坑问题

坑一:workerId 重复

在分布式部署下:

  • 如果 多个实例的 workerId 相同
  • 且在同一毫秒内生成 ID

则会导致:

生成的 ID 完全相同,直接主键冲突

正确做法: 保证同一时刻在线的所有实例,workerId 全局唯一

坑二:系统时间回拨

如果服务器发生 时间回退(如 NTP 同步、虚拟机迁移):

当前时间 < 上一次生成 ID 的时间
1

则可能出现:

新生成的 ID 比旧 ID 小,导致重复

正确做法, 生产环境必须:

  • 增加时间回拨保护
  • 或直接拒绝生成 ID 并报警

# 三、绑定表

绑定表指:分片规则完全一致的主表与子表。

例如:

  • product_order(订单表)
  • product_order_item(订单明细表)

二者如果都按照 order_id 分片,则:

这两张表互为 绑定表

绑定表的核心价值:

  • 多表 JOIN 只发生在 同一个分片内
  • 避免出现 跨库 JOIN
  • 不会产生 笛卡尔积
  • 查询性能大幅提升

实战原则:

只要是强关联表,必须设计成绑定表

# 四、广播表

广播表指:在每一个分片库中都完整存储的一张公共表。

特点:

  • 表结构完全一致
  • 表数据完全一致
  • 每个库各维护一份副本

典型场景:

  • 字典表(状态、类型、枚举)
  • 配置表(系统配置、广告配置)
  • 公共维表(国家、币种、行政区划)

优点:

  • 避免跨库 JOIN
  • 查询时始终走本库
  • 逻辑更简单,性能更稳定

最佳实践:

使用分库分表中间件时,表名、字段名尽量避免使用 SQL 关键字,否则容易产生难以排查的 SQL 路由异常。

# 五、配置实战(分库分表 + 绑定表 + 广播表)

本示例基于如下结构:

创建两个物理库:

  • sharding_order_0
  • sharding_order_1

物理表结构:

表类型 表名
主表 product_order_0 / product_order_1
绑定表 product_order_item_0 / product_order_item_1
广播表 ad_config

分片策略:

  • 分库策略:按照 user_id % 2
  • 分表策略:按照 product_order.id % 2
  • 订单表与明细表为绑定表
  • 广告配置表为广播表
  • 主键采用 Snowflake 雪花算法

# 六、ShardingSphere 分库分表配置

server:
  port: 8080

spring:
  application:
    name: test-shardingJDBC

  main:
    allow-bean-definition-overriding: true

  shardingsphere:
    props:
      sql.show: true

    datasource:
      names: ds0, ds1
      # 物理库 1
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        jdbcUrl: "jdbc:mysql://115.159.195.151:13306/sharding_order_0"
        username: root
        password: MySql123++
      # 物理库 2
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        jdbcUrl: "jdbc:mysql://115.159.195.151:13306/sharding_order_1"
        username: root
        password: MySql123++


    # 在业务代码使用时 只需要使用逻辑表名
    sharding:
      # 未指定分库策略, 则按照默认策略进行分库, ds0/1 用户id%2
      default-database-strategy:
        inline:
          sharding-column: user_id
          algorithm-expression: ds$->{user_id % 2}
      # 表的配置
      tables:
        # 表名
        product_order_item:
          # 分表策略
          table-strategy:
            inline:
              sharding-column: product_order_id
              algorithm-expression: product_order_item_$->{product_order_id % 2}
          # 分库分表策略本质上只是算名的函数, 此处才是声明物理数据库的范围,在该范围内使用对应的分库分表策略
          actual-data-nodes: ds$->{0..1}.product_order_item_$->{0..1}
        product_order:
          table-strategy:
            inline:
              sharding-column: id    # 分表策略 按照用户id取模
              algorithm-expression: product_order_$->{id % 2}
          actual-data-nodes: ds$->{0..1}.product_order_$->{0..1}
          key-generator:  # 自定义主键生成策略
            column: id
            type: SNOWFLAKE
            props:
              worker:
                id: 1

        ad_config:
          key-generator:
            type: SNOWFLAKE
            column: id
      # 广播表
      broadcast-tables: ad_config
      # 绑定表
      binding-tables:
        - product_order, product_order_item
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72