[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生成,为分布式系统提供了可靠的基础设施。

相关推荐
Suhan422 小时前
IDEA 设置启动参数和环境变量
spring boot·spring·编辑器·idea
記億揺晃着的那天4 小时前
NAS + Tailscale + WebDAV:安全对接云服务器 Spring Boot
spring boot·tailscale·nas·webdav
橘子海全栈攻城狮5 小时前
【最新源码】基于SpringBoot + Vue的超市管理系统的设计与实现D002
java·开发语言·vue.js·spring boot·后端·spring
其美杰布-富贵-李6 小时前
Spring Boot 中controller、service、mapper
spring boot·python
拾贰_C6 小时前
【Java | SpringBoot | GlobalExceptionHandler】全局异常拦截器实现思路
java·开发语言·spring boot
Lyra_Infra7 小时前
故障排查报告:Spring boot连接 Nacos 异常及启动失败
java·spring boot
wuqingshun3141598 小时前
如何理解Spring Boot中的starter?
java·spring boot·后端
wuqingshun3141598 小时前
SpringBoot是如何实现自动配置的
java·spring boot·后端
zhouhui0018 小时前
AI帮我写了个Spring Boot校验,线上漏掉了这组边界条件
java·spring boot·redis·ai编程
hdsoft_huge9 小时前
SpringBoot系列17:日志体系Logback配置,日志分割、脱敏、异常堆栈收集规范(生产级落地)
java·spring boot·logback