[022][数据模块]基于雪花算法的 MyBatis-Plus 主键生成器设计与实现
![[022][数据模块]基于雪花算法的 MyBatis-Plus 主键生成器设计与实现](/uploadfile/2026/0707/3ab6372dc1dc908bec6e9107ef60eaf8.webp)
在分布式系统架构中,数据库主键 ID 的生成始终是一个绕不开的技术难题。传统自增主键在分库分表环境下会直接失效,而 UUID 虽然具备全局唯一性,却因存储空间大、无序性高导致索引性能严重下降。相比之下,Twitter 开源的雪花算法(Snowflake)生成的 64 位 Long 型 ID,既能确保全局唯一,又能保持时间有序,因此成为分布式 ID 生成的主流方案。
本文将分享一套经过实际项目验证的解决方案,它深度整合了 Hutool 的雪花算法与 MyBatis-Plus 框架,核心由以下三个组件构成:
SnowflakeUtils:雪花算法工具类,采用单例封装并支持系统属性配置。DefaultIdentifierGenerator:实现 MyBatis-Plus 的IdentifierGenerator接口,使雪花 ID 能够无缝集成。DataMybatisPlusConfiguration:自动配置类,同时注册分页插件、乐观锁插件及防全表更新拦截器。
1. 雪花算法工具类:SnowflakeUtils
设计目标
该工具类需要解决的核心需求非常明确:对外提供静态方法 nextId() 和 nextIdStr(),业务方直接调用即可获得唯一 ID;支持通过 JVM 启动参数动态设置 workerId 和 datacenterId,便于多节点分布式部署;同时采用单例模式避免重复初始化损耗,保证高并发下的性能稳定。
核心代码解析
SnowflakeUtils 的实现非常简洁:定义了两个常量 TUTORIALS4J_SNOWFLAKE_WORKER_ID 和 TUTORIALS4J_SNOWFLAKE_DATACENTER_ID 作为系统属性键,构造函数中调用 initSnowflake() 从系统属性读取值,默认值设为 1。利用 Hutool 的 IdUtil.getSnowflake() 直接创建雪花对象,省去了手动实现位运算的繁琐。异常处理方面也做了周密设计:当传入的属性值非数字时,直接抛出 FrameworkRuntimeException,防止静默失败导致数据重复。
使用示例
long id = SnowflakeUtils.nextId(); 或 String idStr = SnowflakeUtils.nextIdStr();,调用方式极其简单。需要特别说明的是,nextIdStr() 内部仍使用雪花算法生成 ID 后转换为字符串形式,并非 UUID 生成逻辑。
2. MyBatis-Plus 标识生成器适配:DefaultIdentifierGenerator
MyBatis-Plus 提供的 IdentifierGenerator 接口为自定义主键策略开了绿灯。这里的实现很直接:nextId() 直接返回 SnowflakeUtils.nextId(),nextUUID() 返回 SnowflakeUtils.nextIdStr()。前者对应 @TableId(type = IdType.ASSIGN_ID) 且字段类型为数值的场景,后者则对应字符串类型字段。不过 nextUUID 这个命名确实容易产生误解——生成的并非真正的 UUID,而是雪花 ID 的字符串形态,后续将给出改进建议。
3. MyBatis-Plus 自动配置:DataMybatisPlusConfiguration
该配置类在 Spring Boot 环境下自动生效,负责注册全局 ID 生成器。通过 MybatisPlusPropertiesCustomizer 将 DefaultIdentifierGenerator 注入全局配置,之后所有标注了 IdType.ASSIGN_ID 的实体类将自动采用雪花算法,无需在每个实体上重复声明。
4. 整体工作流程
整体流程清晰明了:Spring Boot 启动 → 加载配置类 → 设置全局 ID 生成器 → 业务层插入数据时,MyBatis-Plus 检测到主键策略为 ASSIGN_ID → 调用 DefaultIdentifierGenerator.nextId() → 最终执行 SnowflakeUtils.nextId() → 输出全局唯一且有序的分布式 ID。
5. 部署配置说明
在多节点部署场景下,需要为每个节点分配独一无二的 workerId 与 datacenterId。推荐通过 JVM 参数进行注入:-DTUTORIALS4J_SNOWFLAKE_WORKER_ID=1 -DTUTORIALS4J_SNOWFLAKE_DATACENTER_ID=1。这两个值的范围均为 0~31(Hutool 默认支持 5 bit,如需更大范围可自行扩展)。核心原则是:所有节点的组合必须全局唯一,否则可能生成重复的 ID。
6. 优缺点分析
优势
- 全局唯一且有序:雪花 ID 基于时间戳生成,作为数据库主键天然适配 B+ 树索引结构。
- 高性能:本地生成,零网络开销,单机每秒可达到百万级生成能力。
- 配置灵活:通过系统属性即可控制 worker 和 datacenter 参数,非常适合容器化部署场景。
注意事项
- 时钟回拨问题:Hutool 的
Snowflake默认在检测到时钟回拨时抛出异常,生产环境需确保 NTP 配置正确,或改用容忍回拨的改进版本。 - workerId 分配:在 Kubernetes 等动态 IP 环境中,建议借助 ZooKeeper、Redis 等外部组件实现 workerId 自动分配。
- 方法命名:
nextUUID这个命名容易让人误以为是 UUID,建议重命名为nextIdStr,避免混淆。
7. 扩展与改进建议
- 支持时钟回拨容忍:可参考百度的 UidGenerator 或美团的 Leaf 方案,增加缓冲机制,允许一定范围内的时钟回拨。
- 自动注册 workerId:基于 Redis 或 etcd 实现 workerId 动态分配与心跳续期,彻底解放运维工作。
- 细化配置方式:允许通过
application.yml直接配置 workerId,而不仅限于系统属性。
8. 总结
这套“三件套”方案堪称轻量级、可投产的分布式主键解决方案。开发者只需引入依赖、配置好 JVM 参数,便能享受雪花算法的高性能与全局有序性,同时借助 MyBatis-Plus 的拦截器增强数据库操作安全性。目前该方案已在多个微服务项目中稳定运行,特别适用于中大规模分布式系统的主键生成场景。
