承接上一篇《雪花算法 vs UUIDv7 分库分表 ID 选型对比》,很多同学看完选型后最关心:在 ShardingSphere-JDBC / Proxy 里到底怎么落地?两者建表语句、分片配置、生成方式完全不一样,尤其是 UUIDv7,ShardingSphere没有内置生成器,和雪花的用法有本质区别。本文给可直接复制运行的建表SQL + SpringBoot YAML配置,同时把路由、转换、踩坑点一次性讲清楚。
前置约定:
- 分片拓扑:2个数据库
ds0、ds1;每个库2张表,t_order_0、t_order_1,共2库4表- 最佳原则:主键 ≠ 分片键 ,优先使用业务字段
user_id作为分片键,不要把主键拿来做分片- 雪花: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转字符串查看 |
四、高频踩坑清单(生产必看)
雪花坑
- worker-id重复 → 全局主键冲突,分库分表直接报唯一索引异常;容器动态Pod不要硬编码workerId,要对接注册中心
- 服务器时钟回拨:原生雪花无防护,会生成重复ID,业务层必须捕获、阻塞或告警
- 绑定表、广播表主键统一:order、order_item都用雪花,workerId隔离
UUIDv7坑
- 存CHAR(36):索引膨胀3倍以上,千万级数据内存压力巨大,强制BINARY(16)
- 误以为ShardingSphere能自动生成UUIDv7:配置SNOWFLAKE/UUID只能拿到v4,不是v7
- 前端直接返回二进制:接口要返回hex字符串,后端自行做HEX/UNHEX转换
- 同毫秒无序:不能依赖order_id的大小作为业务排序依据,排序必须依赖created_at时间字段
通用坑
- 分片键和主键混淆:很多新手直接拿主键分片,后续分片扩容、冷热分离很难做;业务分片键尽量独立
- 主键没有在所有分片表上建立主键:每一张真实物理表都要有PRIMARY KEY
- 跨分片全主键查询:无论雪花还是UUIDv7,如果分片键不是主键,根据主键查询会广播路由(扫描所有分片),性能差。解决方案:单独建立映射表(id -> user_id)
五、选型落地总结
- 老系统升级、存量BIGINT主键、数据库资源紧张、不想改代码:选雪花,用好ShardingSphere内置生成器,做好workerId和时钟监控。
- 云原生K8s弹性Pod、多语言异构服务、不想维护发号器、新项目:选UUIDv7,应用层生成 + BINARY(16)存储,分片键独立使用user_id等业务字段。
- 不管选哪一个,优先业务字段分片,主键只负责全局唯一,这是分库分表最稳健的设计范式。