分库分表的 9种分布式主键ID 生成方案,挺全乎的

【分库分表的 9种分布式主键ID 生成方案,挺全乎的】《sharding-jdbc 分库分表的 4种分片策略》 中我们介绍了 sharding-jdbc 4种分片策略的使用场景,可以满足基础的分片功能开发,这篇我们来看看分库分表后,应该如何为分片表生成全局唯一的主键 ID 。
引入任何一种技术都是存在风险的,分库分表当然也不例外,除非库、表数据量持续增加,大到一定程度,以至于现有高可用架构已无法支撑,否则不建议大家做分库分表,因为做了数据分片后,你会发现自己踏上了一段踩坑之路,而分布式主键 ID 就是遇到的第一个坑 。
不同数据节点间生成全局唯一主键是个棘手的问题,一张逻辑表 t_order 拆分成多个真实表 t_order_n,然后被分散到不同分片库 db_0、db_1... ,各真实表的自增键由于无法互相感知从而会产生重复主键,此时数据库本身的自增主键,就无法满足分库分表对主键全局唯一的要求 。
 db_0--    |-- t_order_0    |-- t_order_1    |-- t_order_2 db_1--    |-- t_order_0    |-- t_order_1    |-- t_order_2尽管我们可以通过严格约束,各个分片表自增主键的 初始值 和 步长 的方式来解决 ID 重复的问题,但这样会让运维成本陡增,而且可扩展性极差,一旦要扩容分片表数量,原表数据变动比较大,所以这种方式不太可取 。
 步长 step = 分表张数 db_0--    |-- t_order_0  ID: 0、6、12、18...    |-- t_order_1  ID: 1、7、13、19...    |-- t_order_2  ID: 2、8、14、20... db_1--    |-- t_order_0  ID: 3、9、15、21...    |-- t_order_1  ID: 4、10、16、22...    |-- t_order_2  ID: 5、11、17、23...目前已经有了许多第三方解决方案可以完美解决这个问题,比如基于 UUID、SNOWFLAKE算法 、segment号段,使用特定算法生成不重复键,或者直接引用主键生成服务,像美团(Leaf)和 滴滴(TinyId)等 。
而sharding-jdbc 内置了两种分布式主键生成方案,UUID、SNOWFLAKE,不仅如此它还抽离出分布式主键生成器的接口,以便于开发者实现自定义的主键生成器,后续我们会在自定义的生成器中接入 滴滴(TinyId)的主键生成服务 。
前边介绍过在 sharding-jdbc 中要想为某个字段自动生成主键 ID,只需要在 Application.properties 文件中做如下配置:
# 主键字段spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id# 主键ID 生成方案spring.shardingsphere.sharding.tables.t_order.key-generator.type=UUID# 工作机器 idspring.shardingsphere.sharding.tables.t_order.key-generator.props.worker.id=123key-generator.column 表示主键字段,key-generator.type 为主键 ID 生成方案(内置或自定义的),key-generator.props.worker.id 为机器ID,在主键生成方案设为 SNOWFLAKE 时机器ID 会参与位运算 。

在使用 sharding-jdbc 分布式主键时需要注意两点:
  • 一旦 insert 插入操作的实体对象中主键字段已经赋值,那么即使配置了主键生成方案也会失效,最后SQL 执行的数据会以赋的值为准 。
  • 不要给主键字段设置自增属性,否则主键ID 会以默认的 SNOWFLAKE 方式生成 。比如:用 mybatis plus 的 @TableId 注解给字段 order_id 设置了自增主键,那么此时配置哪种方案,总是按雪花算法生成 。


    推荐阅读