MyBatis-Plus 主键生成策略:雪花算法与自增主键如何抉择?

430 阅读5分钟

在 MyBatis-Plus(简称 MP)中,主键生成策略是实体类设计的核心环节之一,直接影响数据入库的唯一性和效率。其中,雪花算法(Snowflake)  和自增主键(Auto-Increment)  是最常用的两种策略。我们在实际场景中如何选择呢?请跟我一块来看看吧!

一、MyBatis-Plus 主键生成策略概述

MP 提供了@TableId注解用于指定实体类的主键字段,并通过type属性配置生成策略。其内置的主键策略定义在IdType枚举类中,常见类型包括:

  • AUTO:数据库自增(依赖数据库支持)
  • NONE:未设置策略(需手动赋值)
  • INPUT:手动输入(需用户自行设置主键)
  • ASSIGN_ID:默认策略,使用雪花算法生成 Long 型主键
  • ASSIGN_UUID:生成 UUID 字符串主键(32 位,不含中划线)

其中,AUTO(自增)和ASSIGN_ID(雪花算法)是实际开发中最常用的两种,下面重点说明。

二、自增主键(IdType.AUTO)

1. 原理

自增主键依赖数据库的自增机制:在表结构中指定主键字段为AUTO_INCREMENT(MySQL)或SERIAL(PostgreSQL),插入数据时数据库会自动为该字段分配一个递增的唯一值(通常从 1 开始,每次 + 1)。

2. 使用条件

  • 数据库必须支持自增特性(如 MySQL、PostgreSQL 支持,Oracle 默认不支持)。
  • 表结构中主键字段需显式配置自增(如 MySQL 中id INT PRIMARY KEY AUTO_INCREMENT)。

3. MP 中配置方式

步骤 1:实体类注解配置
在主键字段上添加@TableId(type = IdType.AUTO)

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;

public class User {
    // 指定主键生成策略为自增
    @TableId(type = IdType.AUTO)
    private Long id; 
    private String name;
    // 其他字段...
}

步骤 2:数据库表配置
确保表的主键字段设置为自增(以 MySQL 为例):

CREATE TABLE user (
    id BIGINT(20) PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    name VARCHAR(30) NOT NULL COMMENT '用户名'
    -- 其他字段...
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

4. 优缺点

  • 优点

    • 实现简单,依赖数据库原生机制,无需额外代码。
    • 主键值递增,索引效率高(B + 树索引对有序值友好)。
  • 缺点

    • 强依赖数据库,跨库迁移时可能存在兼容性问题(如 Oracle 需额外配置序列)。
    • 分布式场景下,多库实例无法保证全局唯一(需额外处理如分库分表时的偏移量)。
    • 插入前无法获取主键值(需插入后通过SELECT 6754882获取)。

三、雪花算法(IdType.ASSIGN_ID)

1. 原理

雪花算法是 Twitter 开源的分布式 ID 生成算法,其核心是生成一个64 位的 Long 型整数,结构如下:

1位符号位(固定0,保证为正数) + 41位时间戳(毫秒级,可支持约69年) + 10位机器码(可支持1024个节点) + 12位序列号(同一毫秒内最多生成4096个ID)
  • 时间戳:从指定起始时间(如 1970-01-01)到当前时间的毫秒数,确保 ID 整体递增。
  • 机器码:由部署节点的 ID(如服务器 IP 哈希)生成,避免分布式环境下的 ID 冲突。
  • 序列号:同一毫秒内,同一节点生成的 ID 序列号,确保毫秒内唯一。

MP 默认集成了雪花算法,无需额外配置即可使用。

2. 使用条件

  • 适用于分布式系统,需保证每个节点的机器码唯一(MP 默认通过InetAddress获取 IP 生成机器码,可自定义)。
  • 主键字段类型需为Long(64 位)或String(存储为字符串形式的 Long 值)。

3. MP 中配置方式

步骤 1:实体类注解配置
ASSIGN_ID是 MP 的默认策略,可省略type属性:

public class User {
    // 默认为ASSIGN_ID(雪花算法),可省略type属性
    @TableId
    private Long id; 
    private String name;
    // 其他字段...
}

步骤 2:自定义机器码(可选)
若分布式环境中节点较多(超过 1024 个),或需手动指定机器码,可通过配置类自定义:

import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisPlusConfig {
    // 自定义雪花算法的机器码(如指定workerId=1,datacenterId=1)
    @Bean
    public IdentifierGenerator identifierGenerator() {
        return new DefaultIdentifierGenerator(1L, 1L); 
    }
}

4. 优缺点

  • 优点

    • 分布式友好,全局唯一,无需依赖数据库。
    • 主键值递增(基于时间戳),索引效率高。
    • 插入前可获取主键值(生成后直接赋值给实体)。
  • 缺点

    • 依赖系统时间,若时钟回拨可能导致 ID 重复(MP 已做时钟回拨检测处理)。
    • 主键为 Long 型,若需字符串 ID 需额外转换(可使用ASSIGN_UUID替代)。

四、两种策略的对比与选择

维度自增主键(AUTO)雪花算法(ASSIGN_ID)
依赖数据库自增机制无(算法生成)
分布式支持弱(多库需额外处理)强(天然全局唯一)
主键类型整数(INT/BIGINT)长整数(Long)
插入前获取 ID不能(需插入后查询)能(生成后直接赋值)
兼容性依赖数据库(如 MySQL 支持,Oracle 需配置)跨数据库兼容

选择建议:

  • 单体应用、数据库单一且支持自增:优先使用自增主键(简单高效)。
  • 分布式系统、多数据库实例或需要插入前获取 ID:优先使用雪花算法

五、扩展:其他主键策略

  • ASSIGN_UUID:生成 32 位 UUID 字符串(如f47ac10b58cc4372a56729ca0f8db38),无需依赖数据库,适合字符串主键场景,但索引效率略低于整数。
  • INPUT:手动输入主键,适用于主键由业务逻辑生成的场景(如订单号)。

总结

MyBatis-Plus 的主键生成策略为开发者提供了灵活选择:自增主键适合简单场景,依赖数据库原生能力;雪花算法则是分布式环境的首选,通过算法保证全局唯一。

实际开发中需根据系统架构、数据库类型及业务需求综合选择,必要时可自定义主键生成逻辑。