ShardingSphere 雪花 / UUIDv7 主键分片实战 + SQL 建表示例

承接上一篇《雪花算法 vs UUIDv7 分库分表 ID 选型对比》,很多同学看完选型后最关心:在 ShardingSphere-JDBC / Proxy 里到底怎么落地?两者建表语句、分片配置、生成方式完全不一样,尤其是 UUIDv7,ShardingSphere没有内置生成器,和雪花的用法有本质区别。本文给可直接复制运行的建表SQL + SpringBoot YAML配置,同时把路由、转换、踩坑点一次性讲清楚。
前置约定:

  1. 分片拓扑:2个数据库 ds0、ds1;每个库2张表,t_order_0、t_order_1,共2库4表
  2. 最佳原则:主键 ≠ 分片键 ,优先使用业务字段 user_id 作为分片键,不要把主键拿来做分片
  3. 雪花:ShardingSphere内置生成器,框架层自动生成;UUIDv7:应用层生成,框架只负责分片路由

一、方案A:雪花算法(BIGINT主键)实战

1. MySQL 建表SQL(每个分片库都执行)

sql 复制代码
CREATE TABLE `t_order_0` (
  `order_id` BIGINT NOT NULL COMMENT '雪花主键',
  `user_id` BIGINT NOT NULL COMMENT '分片键',
  `order_no` VARCHAR(32) NOT NULL,
  `amount` DECIMAL(12,2) NOT NULL,
  `status` TINYINT NOT NULL DEFAULT 0,
  `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  PRIMARY KEY (`order_id`),
  KEY idx_user_id (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `t_order_1` LIKE t_order_0;

要点:主键类型 BIGINT,8字节,和雪花64bit一一对应。插入时不用业务代码set order_id,交给ShardingSphere自动生成。

2. SpringBoot YAML 分片配置(ShardingSphere 5.x)

yaml 复制代码
spring:
  shardingsphere:
    datasource:
      names: ds0,ds1
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/db0?useUnicode=true&characterEncoding=utf8mb4
        username: root
        password: 123456
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf8mb4
        username: root
        password: 123456
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
            # 分片策略:使用 user_id 分库分表,和主键解耦
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: db-inline
            table-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: tbl-inline
            # 分布式主键配置:雪花
            key-generate-strategy:
              column: order_id
              key-generator-name: snowflake-gen
        sharding-algorithms:
          db-inline:
            type: INLINE
            props:
              algorithm-expression: ds$->{user_id % 2}
          tbl-inline:
            type: INLINE
            props:
              algorithm-expression: t_order_$->{user_id % 2}
        key-generators:
          snowflake-gen:
            type: SNOWFLAKE
            props:
              worker-id: 1
              data-center-id: 0
    props:
      sql-show: true

关键说明:

  • worker-id 必须保证集群全局唯一,0~1023,冲突直接主键重复;K8s动态容器不友好,这也是雪花最大痛点
  • 插入SQL只需要给user_id、amount等业务字段,order_id框架自动填充
sql 复制代码
-- 写入SQL示例,不需要写order_id
INSERT INTO t_order(user_id,order_no,amount,status) VALUES (10001,'ORD20261020001',99.00,1);

3. 雪花如果强行用主键order_id做分片(不推荐)

只需要修改分片列:sharding-column: order_id,表达式改成 order_id % 2。

⚠️ 风险:雪花内置workerId,扩容后容易数据倾斜;业务尽量不要这么做。

二、方案B:UUIDv7(BINARY(16)主键)实战

重点提醒:ShardingSphere 官方没有内置UUIDv7生成器 ,不能像雪花那样配置key-generator自动生成,必须在Java/Go等业务代码提前生成好UUIDv7二进制值,传给SQL。

❌ 禁止:CHAR(36)/VARCHAR(36),存储体积大、索引慢、排序差

✅ 标准:BINARY(16)

1. MySQL 建表SQL(每个分片库执行)

sql 复制代码
CREATE TABLE `t_order_0` (
  `order_id` BINARY(16) NOT NULL COMMENT 'UUIDv7主键,二进制存储',
  `user_id` BIGINT NOT NULL COMMENT '分片键',
  `order_no` VARCHAR(32) NOT NULL,
  `amount` DECIMAL(12,2) NOT NULL,
  `status` TINYINT NOT NULL DEFAULT 0,
  `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  PRIMARY KEY (`order_id`),
  KEY idx_user_id (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `t_order_1` LIKE t_order_0;

2. YAML分片配置(没有key-generator)

yaml 复制代码
spring:
  shardingsphere:
    datasource:
      names: ds0,ds1
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/db0?useUnicode=true&characterEncoding=utf8mb4
        username: root
        password: 123456
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf8mb4
        username: root
        password: 123456
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: db-inline
            table-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: tbl-inline
            # 【重点】不配置key-generate-strategy,主键由应用传入
        sharding-algorithms:
          db-inline:
            type: INLINE
            props:
              algorithm-expression: ds$->{user_id % 2}
          tbl-inline:
            type: INLINE
            props:
              algorithm-expression: t_order_$->{user_id % 2}
    props:
      sql-show: true

3. SQL读写示例(UUIDv7二进制转换)

Java侧生成UUIDv7,转byte\[\],直接写入BINARY(16)字段。

调试时可以用 HEX() / UNHEX() 查看:

sql 复制代码
-- 查询,转16进制方便查看
SELECT HEX(order_id) AS order_id_hex, user_id, amount FROM t_order WHERE user_id = 10001;

-- 测试写入(仅调试,生产在代码生成UUIDv7二进制)
INSERT INTO t_order(order_id,user_id,order_no,amount,status) 
VALUES (UNHEX('01962f32a12c70008000000000000001'),10002,'ORD20261020002',199.00,1);

坑点:不要用MySQL自带 UUID(),它是v1版本,不是v7。MySQL没有原生UUIDv7函数。

4. 如果业务非要拿UUIDv7主键做分片

UUIDv7是BINARY(16),INLINE算法不能直接对二进制取模,需要自定义分片算法:把二进制转为BigInteger后哈希取模。

不推荐!UUIDv7没有内置业务信息,哈希分片完全随机,无法定向路由;优先保持user_id分片。

三、两种方案的代码层差异对比

项目 雪花算法 UUIDv7(BINARY16)
生成位置 ShardingSphere框架自动生成 应用层SDK生成
主键类型 BIGINT BINARY(16)
配置 配置keyGenerator、workerId 无主键生成配置
插入代码 不用set主键 代码生成byte\[\],set主键
扩容难点 WorkerID分配、冲突、时钟回拨 无协调,弹性扩缩友好
调试 直接数字,可读性高 需要HEX转字符串查看

四、高频踩坑清单(生产必看)

雪花坑

  1. worker-id重复 → 全局主键冲突,分库分表直接报唯一索引异常;容器动态Pod不要硬编码workerId,要对接注册中心
  2. 服务器时钟回拨:原生雪花无防护,会生成重复ID,业务层必须捕获、阻塞或告警
  3. 绑定表、广播表主键统一:order、order_item都用雪花,workerId隔离

UUIDv7坑

  1. 存CHAR(36):索引膨胀3倍以上,千万级数据内存压力巨大,强制BINARY(16)
  2. 误以为ShardingSphere能自动生成UUIDv7:配置SNOWFLAKE/UUID只能拿到v4,不是v7
  3. 前端直接返回二进制:接口要返回hex字符串,后端自行做HEX/UNHEX转换
  4. 同毫秒无序:不能依赖order_id的大小作为业务排序依据,排序必须依赖created_at时间字段

通用坑

  1. 分片键和主键混淆:很多新手直接拿主键分片,后续分片扩容、冷热分离很难做;业务分片键尽量独立
  2. 主键没有在所有分片表上建立主键:每一张真实物理表都要有PRIMARY KEY
  3. 跨分片全主键查询:无论雪花还是UUIDv7,如果分片键不是主键,根据主键查询会广播路由(扫描所有分片),性能差。解决方案:单独建立映射表(id -> user_id)

五、选型落地总结

  1. 老系统升级、存量BIGINT主键、数据库资源紧张、不想改代码:选雪花,用好ShardingSphere内置生成器,做好workerId和时钟监控。
  2. 云原生K8s弹性Pod、多语言异构服务、不想维护发号器、新项目:选UUIDv7,应用层生成 + BINARY(16)存储,分片键独立使用user_id等业务字段。
  3. 不管选哪一个,优先业务字段分片,主键只负责全局唯一,这是分库分表最稳健的设计范式。
相关推荐
Elastic 中国社区官方博客1 小时前
Elasticsearch:安装自行管理的 Elastic Stack 集群
大数据·运维·服务器·数据库·elasticsearch·搜索引擎·全文检索
PingCAP2 小时前
Agent 从 Demo 到生产,到底卡在哪?
数据库·tidb·agent
SelectDB3 小时前
开源替代怎么选:能力对照、三条部署路径与四类边界速查
数据库·云原生·开源
SelectDB3 小时前
自建与托管怎么选:开源自建、私有化与云上托管三条路径速查
数据库·云原生·开源
这个DBA有点耶3 小时前
深入 OLTP 锁与并发:从行锁到分布式事务的一致性权衡
数据库·架构·产品
SelectDB3 小时前
数仓 TCO 怎么算:五项成本拆解框架速查
大数据·数据库·开源
虚拟世界AI3 小时前
Kronos 时间序列数据库新手部署与实战指南
数据库
愤怒火龙果3 小时前
AI攻防 AIRT03-RAG Exploitation & Vector Database Attacks
数据库·人工智能·网络安全
SelectDB4 小时前
高并发 BI 看板怎么撑住:隔离与加速配置速查
大数据·数据库·数据分析