[041][公共模块]分布式唯一ID生成器设计与实现:一款灵活可扩展的雪花算法框架

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接口定义标准操作,UidDefaultedGeneratorUidCachedGenerator分别实现两种模式。
  • 定制层DefaultedUidGeneratorCustomizerCachedUidGeneratorCustomizer函数接口,允许用户在Spring Bean定义阶段修改生成器属性。
  • Spring集成层UidCommonConfiguration自动配置两个生成器Bean,并从ObjectProvider中收集所有定制器并应用。
  • ORM集成层 :MyBatis-Plus的IdentifierGenerator实现类UidentifierGenerator,Hibernate的@UidGenerator注解及对应的IdentifierGenerator实现。
  • 工具门面层UidUtils静态工具类,提供DEFAULTEDCACHED两个静态实例,供非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-typeASSIGN_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生成,为分布式系统提供了可靠的基础设施。

相关推荐
Flittly1 天前
【AgentScope Java新手村系列】(14)人机交互
java·spring boot·spring
Flynt2 天前
从Spring Boot 4.0升到4.1,我在Maven和gRPC上栽了跟头
java·spring boot·后端
掉鱼的猫3 天前
Spring Boot → Solon 注解迁移实战指南:一张对照表说清楚
java·spring boot
人活一口气4 天前
Spring Boot与AIGC的完美结合:从零搭建智能内容生成平台
java·spring boot·aigc
java小白小7 天前
SpringBoot(01): 初识SpringBoot,从Spring的痛点说起
spring boot
用户3169353811837 天前
如何从零编写一个 Spring Boot Starter
spring boot
程序员晓琪8 天前
约定大于配置:基于 Java 包名自动生成 API 版本路由的最佳实践
java·spring boot·后端
Flittly8 天前
【AgentScope Java新手村系列】(11)中断与恢复
java·spring boot·spring
用户3521802454759 天前
🎆从 Prompt 到 Skill:让 Spring AI Agent 学会"装新技能"
人工智能·spring boot·ai编程