MyBatis Plus 主键策略实战:从AUTO到UUID的深度解析与应用场景

张开发
2026/4/17 4:59:19 15 分钟阅读

分享文章

MyBatis Plus 主键策略实战:从AUTO到UUID的深度解析与应用场景
1. MyBatis Plus主键策略全景解析第一次用MyBatis Plus做用户表新增时发现自动生成的ID居然是19位的长数字当时我就懵了——这跟我想象中的1、2、3自增序列完全不一样。后来才发现这背后藏着主键生成策略的大学问。今天我们就来彻底搞懂MyBatis Plus的五种主键策略帮你避开我当年踩过的坑。先看个真实案例去年做电商系统时订单表开始用的数据库自增ID结果分库分表时ID疯狂冲突。后来改用雪花算法线上跑了半年多一直很稳。主键策略选对了真的能省去很多麻烦。MyBatis Plus主要提供这些策略AUTO数据库自增单机环境首选INPUT手动输入适合数据迁移场景ASSIGN_ID雪花算法分布式系统标配ASSIGN_UUID全局唯一但性能较差NONE相当于INPUT的备胎理解这些策略的关键是要明白不同业务场景对ID的核心要求订单系统需要趋势递增的ID方便分页查询日志表更看重写入速度对ID连续性无要求分布式系统必须保证全局唯一不能有冲突关联业务可能需要可读性强的ID方便人工核对2. AUTO策略单机环境的最佳拍档2.1 配置与使用实战最近给本地CMS系统用户表配置AUTO策略时走了些弯路。首先要在实体类这样声明Data public class User { TableId(type IdType.AUTO) private Long id; // 其他字段... }关键点来了必须确保数据库表的主键设置了自增用MySQL的话建表语句要有这个CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, ... );常见坑点当你的表里已有ID较大的记录比如删过数据新增时可能会出现跳跃ID。这时需要重置自增计数器ALTER TABLE user AUTO_INCREMENT1;2.2 内部实现原理AUTO策略本质是MyBatis Plus执行insert后通过JDBC的getGeneratedKeys方法获取数据库生成的值。我翻过源码关键逻辑在AbstractMethod.executeAutoIncrement里执行INSERT语句时不带ID值通过Statement.RETURN_GENERATED_KEYS参数告诉JDBC需要返回生成的键从ResultSet中读取数据库生成的主键性能方面在本地测试中插入10万条记录AUTO策略耗时约12秒预先生成ID的ASSIGN_ID策略约8秒3. INPUT策略掌控ID生成主动权3.1 特殊业务场景解决方案上个月做老旧系统迁移时INPUT策略帮了大忙。需要将原有Oracle数据库的ID原封不动迁移到MySQL配置是这样的public class LegacyOrder { TableId(type IdType.INPUT) private String customId; // 原系统的特殊ID格式 }使用时必须显式设置ID值LegacyOrder order new LegacyOrder(); order.setCustomId(OLD_2023_0001); // 保持与原系统一致 orderDao.insert(order);特别注意使用INPUT策略时数据库表不能设置自增否则会报错。推荐建表时去掉AUTO_INCREMENTCREATE TABLE legacy_order ( custom_id VARCHAR(32) PRIMARY KEY, -- 注意没有AUTO_INCREMENT ... );3.2 防踩坑指南在测试环境遇到过这个问题忘记设置ID直接insert报错信息很直接java.sql.SQLException: Field id doesnt have a default value解决方案有三严格检查所有insert操作是否设置ID推荐数据库设置默认值不适用于业务ID改用AUTO或ASSIGN_ID策略4. ASSIGN_ID分布式系统的黄金选择4.1 雪花算法深度剖析去年做物联网平台时每天要处理百万级设备上报数据。ASSIGN_ID的雪花算法表现非常出色先看配置public class DeviceLog { TableId(type IdType.ASSIGN_ID) private Long id; }雪花算法的精妙之处在于其64位结构0 | 0001100 10100010 10111110 10001001 01011100 | 00011 | 01101 | 0000 01000100首位固定041位时间戳69年不重复5位数据中心ID5位机器ID12位序列号实测生成性能单机每秒可生成40万 ID比UUID快3倍左右4.2 时钟回拨问题解决方案去年双11大促时遇到过服务器时间同步问题导致生成的ID出现重复。最终我们的解决方案是启用MyBatis Plus的IdentifierGenerator接口自定义实现增加ZooKeeper的分布式序号生成关键业务表添加唯一索引作为兜底配置示例mybatis-plus: global-config: db-config: id-type: assign_id worker-id: ${random.int(1,31)} # 动态分配workerId5. ASSIGN_UUID特殊场景的备选方案5.1 配置注意事项给文件管理系统配置UUID主键时需要特别注意类型转换public class FileRecord { TableId(type IdType.ASSIGN_UUID) private String id; // 必须是String类型 }数据库表结构要对应调整CREATE TABLE file_record ( id VARCHAR(36) PRIMARY KEY, # UUID长度通常为36字符 ... );5.2 性能优化实践在日志系统中对比测试发现插入性能比ASSIGN_ID慢约35%索引大小是长整型的4倍查询效率范围查询速度降低60%优化方案对热数据表使用自增IDUUID组合建立额外的数值型索引列对UUID进行编码压缩存储6. 策略选型决策树根据三年来的实战经验我总结出这个选择流程图是否需要特殊格式ID? ├─ 是 → INPUT策略 └─ 否 → 是否分布式部署? ├─ 是 → 是否需要排序? │ ├─ 是 → ASSIGN_ID │ └─ 否 → ASSIGN_UUID └─ 否 → AUTO典型场景示例用户评论AUTO简单高效分布式订单ASSIGN_ID全局唯一且有序文件存储ASSIGN_UUID无需顺序数据迁移INPUT保持原ID7. 高级配置技巧7.1 全局策略配置在多个微服务项目中这样配置更省事mybatis-plus: global-config: db-config: id-type: assign_id table-prefix: t_ worker-id: ${server.port} # 用端口号作为workerId7.2 自定义ID生成器实现自己的ID生成其实很简单Component public class CustomIdGenerator implements IdentifierGenerator { Override public Number nextId(Object entity) { // 实现你的ID生成逻辑 return Snowflake.nextId(); } }在实体类上指定生成器public class Order { TableId(type IdType.ASSIGN_ID, generator customIdGenerator) private Long id; }

更多文章