041公共模块分布式唯一ID生成器设计与实现:一款灵活可扩展的雪花算法框架
本项目代码: gitee.com/yunjiao-sou...
在分布式系统中,全局唯一ID(如订单号、用户ID)的生成是一个基础且关键的问题。常见的方案包括数据库自增、UUID、Redis自增以及雪花算法(Snowflake)。其中,雪花算法因其高性能、趋势递增、无需依赖外部存储等优点被广泛应用。然而,在实际项目中,我们需要一种与ORM框架(MyBatis-Plus、Hibernate)无缝集成、支持高并发缓存预取、且具备灵活定制能力的统一ID生成方案。本文介绍的一款UID生成器模块正好满足了这些需求。
一、模块概览
该模块基于Java 17、Spring Boot 3.x,封装了底层雪花算法实现(依赖cc.siyecao.uid.core库),提供了两种核心生成器:
- DefaultUidGenerator:实时计算,适合中等并发场景。
- CachedUidGenerator:使用RingBuffer预先生成ID,适合超高并发场景。
同时,模块实现了与MyBatis-Plus和Hibernate的集成,开发者只需简单配置即可自动填充实体类主键。此外,模块提供了定制器(Customizer)接口,允许用户通过配置文件或Lambda表达式调整生成器参数(如时间位数、序列号位数、RingBuffer容量等)。
二、整体架构
模块分为以下几个子包:
python
tutorials4j.framework.common.uid # 核心接口与实现
tutorials4j.framework.common.uid.autoconfigure # Spring自动配置
tutorials4j.framework.data.mybatis # MyBatis-Plus集成
tutorials4j.framework.data.hibernate.id # Hibernate集成
- UID生成核心层 :
UidGenerator接口定义标准操作,UidDefaultedGenerator和UidCachedGenerator分别实现两种模式。 - 定制层 :
DefaultedUidGeneratorCustomizer和CachedUidGeneratorCustomizer函数接口,允许用户在Spring Bean定义阶段修改生成器属性。 - Spring集成层 :
UidCommonConfiguration自动配置两个生成器Bean,并从ObjectProvider中收集所有定制器并应用。 - ORM集成层 :MyBatis-Plus的
IdentifierGenerator实现类UidentifierGenerator,Hibernate的@UidGenerator注解及对应的IdentifierGenerator实现。 - 工具门面层 :
UidUtils静态工具类,提供DEFAULTED和CACHED两个静态实例,供非Spring管理的组件使用。
下图展示了模块的核心依赖关系:
markdown
┌─────────────────┐ ┌──────────────────────┐
│ UidUtils │────▶│ SpringUtil.getBean() │
└────────┬────────┘ └──────────┬───────────┘
│ │
▼ ▼
┌─────────────────────────────────────────┐
│ UidGenerator接口 │
└────────────┬───────────────┬────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│UidDefaultedGenerator│ │UidCachedGenerator│
└────────┬─────────┘ └────────┬─────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│DefaultUidGenerator│ │CachedUidGenerator│
└──────────────────┘ └──────────────────┘
三、核心类详细解析
1. UidGenerator 接口
定义了最基础的三个方法:
long nextUid():生成长整型ID。String nextUidStr():生成字符串形式ID,便于作为主键(特别是字符串类型主键)。String parseUid(long uid):解析ID的时间戳、机器ID、序列号,用于调试。void destroy():释放资源(如CachedUidGenerator的RingBuffer)。
2. 两种实现
UidDefaultedGenerator
使用DefaultUidGenerator(实时计算),每次调用nextUid()都会重新计算雪花ID。优点:实现简单、无内存占用。缺点:高并发下可能因CAS自旋产生性能瓶颈。
初始化关键代码:
java
DefaultUidGenerator gen = new DefaultUidGenerator();
gen.setEpochStr("2026-05-21");
gen.setWorkerIdAssigner(SnowflakeIdProvider.getInstance()::provideWorkerId);
customizers.forEach(customizer -> customizer.customize(gen));
gen.afterPropertiesSet();
epochStr:固定纪元时间点,减少时间戳占用的位数,延长可用年限。workerIdAssigner:从SnowflakeIdProvider获取当前实例的机器ID,支持自动分配(基于IP、MAC等)。
UidCachedGenerator
包装CachedUidGenerator,内部使用RingBuffer预填充ID,显著提升吞吐量。适合高并发写入场景(如订单创建、日志插入)。同样支持自定义RingBuffer大小、填充阈值等。
注意实现了@PreDestroy回调,在Spring容器关闭时调用底层destroy()方法,避免内存泄漏。
3. 定制器机制
两个函数式接口:
java
@FunctionalInterface
public interface DefaultedUidGeneratorCustomizer {
void customize(DefaultUidGenerator generator);
}
用户在任意@Configuration类中声明@Bean方法返回该定制器,模块的自动配置会通过ObjectProvider获取所有定制器并按顺序执行。这样可以在不修改源代码的情况下调整生成器参数,例如:
java
@Bean
public DefaultedUidGeneratorCustomizer myCustomizer() {
return gen -> {
gen.setTimeBits(30);
gen.setSeqBits(12);
gen.setWorkerBits(22);
};
}
4. ORM 集成
MyBatis-Plus 集成
实现com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator接口,委托给UidUtils.DEFAULTED。需要注意:MyBatis-Plus会通过nextId方法生成数字类型主键,nextUUID生成字符串类型主键。我们的实现统一返回雪花ID(数字直接返回Number,字符串返回String.valueOf(uid))。
使用方式:在MyBatis-Plus的全局配置中设置id-type为ASSIGN_ID,框架会自动调用此生成器;或者直接在实体类主键上标注@TableId(type = IdType.ASSIGN_ID)。
Hibernate 集成
实现org.hibernate.id.IdentifierGenerator,并通过@IdGeneratorType注解关联到自定义注解@UidGenerator。生成器构造时通过反射获取主键属性类型(Field或Method的返回类型),运行时根据类型选择调用nextUid()或nextUidStr()。
使用示例:
java
@Entity
public class Order {
@Id
@UidGenerator
private Long id;
}
5. 工具类 UidUtils
提供静态入口,方便非Spring管理的组件(如一些工具类、静态方法)获取生成器。利用Hutool的SpringUtil(需确保Spring上下文已初始化)获取容器中的Bean,并存储到静态内部类holder中,实现懒加载且线程安全。
java
public static final UidGenerator DEFAULTED = DefaultedHolder.INSTANCE;
private static final class DefaultedHolder {
static final UidGenerator INSTANCE = SpringUtil.getBean(UidDefaultedGenerator.class);
}
四、使用指南
1. 引入依赖
xml
<dependency>
<groupId>yunjiao-source.tutorials4j.framework</groupId>
<artifactId>data-spring-boot-starter</artifactId>
<version>${revision}</version>
</dependency>
2. 基础配置
模块自动配置默认开启,无需额外配置。如果需要调整Worker ID分配策略,可以覆盖SnowflakeIdProvider的实现。
3. 自定义生成器参数
java
@Configuration
public class UidCustomizerConfig {
@Bean
public CachedUidGeneratorCustomizer cachedCustomizer() {
return gen -> {
gen.setRingBufferSize(16384);
gen.setBoostPower(3);
};
}
@Bean
public DefaultedUidGeneratorCustomizer defaultedCustomizer() {
return gen -> {
gen.setTimeBits(31);
gen.setSeqBits(10);
};
}
}
4. 在代码中使用
方式一:依赖注入
java
@Service
public class OrderService {
@Autowired
private UidCachedGenerator uidGenerator; // 或 UidDefaultedGenerator
public void create() {
long orderId = uidGenerator.nextUid();
}
}
方式二:静态工具类
java
long id = UidUtils.DEFAULTED.nextUid();
String idStr = UidUtils.CACHED.nextUidStr();
5. 在实体类中使用
MyBatis-Plus
java
@Data
@TableName("user")
public class User {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String name;
}
Hibernate
java
@Entity
public class Product {
@Id
@UidGenerator
private String id; // 字符串类型主键也可以
}
五、性能与注意事项
- CachedUidGenerator 默认RingBuffer大小为8192,建议根据业务峰值流量调整。如果流量极大,可适当增加容量及填充因子。
- 两种生成器都依赖于Worker ID分配器 。本模块使用
SnowflakeIdProvider,默认基于网卡MAC和进程ID计算,确保每个实例获得唯一Worker ID。在多容器部署时需保证不重复。 - 时间回溯问题:底层雪花算法实现具备时间检测功能,若发现系统时钟回拨会抛出异常或等待。生产环境应配合NTP服务谨慎配置。
- 在Spring容器完全启动前(如
@PostConstruct阶段),UidUtils中的SpringUtil.getBean()可能无法获取Bean,建议在Spring管理Bean中通过构造函数注入生成器,或确保在ApplicationRunner之后使用静态实例。
六、总结
本文介绍的UID生成器模块不仅提供了高性能的雪花算法实现,还通过定制器机制、多ORM集成、静态工具类等设计,实现了高内聚、低耦合的架构。开发者可以在不改变核心代码的情况下自由切换两种生成模式,并轻松集成到现有项目中。该模块已在多个生产环境中稳定运行,支撑日均亿级ID生成,为分布式系统提供了可靠的基础设施。