高并发系统-分布式唯一ID生成(五)-百度UidGenerator

紧接着上文高并发系统-分布式唯一ID生成(四)-雪花算法Snowflake

2. 生成方案

2.7 百度-UidGenerator

UidGenerator是Java实现的, 基于Snowflake算法的唯一ID生成器。

UidGenerator以组件形式工作在应用项目中, 支持自定义workerId位数和初始化策略, 从而适用于docker等虚拟化环境下实例自动重启、漂移等场景。

在实现上, UidGenerator通过借用未来时间来解决sequence天然存在的并发限制; 采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费, 同时对CacheLine补齐,避免了由RingBuffer带来的硬件级「伪共享」问题. 最终单机QPS可达600万。

组件方式集成:下载对应源代码集成到实际使用代码中。

依赖版本:Java8及以上版本, MySQL(内置WorkerID分配器, 启动阶段通过DB进行分配; 如自定义实现, 则DB非必选依赖)

Snowflake算法

Snowflake算法描述:指定机器 & 同一时刻 & 某一并发序列,是唯一的。据此可生成一个64 bits的唯一ID(long)。默认采用上图字节分配方式:

  • sign(1bit)
    固定1bit符号标识,即生成的UID为正数。
  • delta seconds (28 bits)
    当前时间,相对于时间基点"2016-05-20"的增量值,单位:秒,最多可支持约8.7年
  • worker id (22 bits)
    机器id,最多可支持约420w次机器启动。内置实现为在启动时由数据库分配,默认分配策略为用后即弃,后续可提供复用策略。
  • sequence (13 bits)
    每秒下的并发序列,13 bits可支持每秒8192个并发。

以上参数均可通过Spring进行自定义

1. 优点

  1. 高性能:uid-generator通过使用RingBuffer和高效的算法实现了高性能的分布式ID生成。它使用了预先分配的ID池来避免每次请求都需要访问数据库或网络,从而提高了生成ID的速度。
  2. 高可用性:uid-generator支持多实例部署,并通过使用Snowflake算法和分布式的WorkerID分配来确保生成的ID在多实例环境下的唯一性。
  3. 灵活的配置:uid-generator提供了多种配置选项,如时间位数、WorkerID位数、序列号位数等,可以根据需求进行调整和优化。
  4. 易于集成:uid-generator提供了与Spring框架和MyBatis集成的支持,可以方便地与现有的Java应用程序进行集成。

2. 缺点

  1. DefaultUidGenerator依赖于机器时钟:雪花算法的实现
  2. 需要预先分配ID范围:为了提高性能,uid-generator需要预先分配ID范围,这可能会导致一些ID浪费。如果系统的ID使用率很低,可能会浪费更多的ID。
  3. 依赖于数据库或配置中心:uid-generator的WorkerID分配依赖于数据库或配置中心,这可能增加了一些外部依赖性,并可能成为系统的瓶颈。
  4. 不适用于大规模ID生成:虽然uid-generator在一般场景下具有良好的性能,但在需要非常高并发和大规模ID生成的场景下,可能会面临一些挑战。在这种情况下,可能需要考虑更复杂的ID生成方案。

3. 应用场景

集成简单,可以应用在并发不是极高的场景下

4. 实践

以下是基于spring boot + mybatics的实现

预制建表SQL

sql 复制代码
use point_shard1;
DROP TABLE IF EXISTS WORKER_NODE;
CREATE TABLE WORKER_NODE
(
ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',
HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name',
PORT VARCHAR(64) NOT NULL COMMENT 'port',
TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER',
LAUNCH_DATE DATE NOT NULL COMMENT 'launch date',
MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time',
CREATED TIMESTAMP NOT NULL COMMENT 'created time',
PRIMARY KEY(ID)
)
 COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;

从官网下载代码,继承到项目中去,下面红框中

增加bean配置

java 复制代码
@Configuration
public class UidConfig {
    @Bean(name = "defaultUidGenerator")
    public DefaultUidGenerator defaultUidGenerator(DisposableWorkerIdAssigner disposableWorkerIdAssigner) {
        DefaultUidGenerator uidGenerator = new DefaultUidGenerator();
        uidGenerator.setWorkerIdAssigner(disposableWorkerIdAssigner);
        uidGenerator.setTimeBits(29);
        uidGenerator.setWorkerBits(21);
        uidGenerator.setSeqBits(13);
        uidGenerator.setEpochStr("2016-09-20");
        return uidGenerator;
    }

    @Bean(name = "cachedUidGenerator")
    public CachedUidGenerator cachedUidGenerator(DisposableWorkerIdAssigner disposableWorkerIdAssigner) {
        CachedUidGenerator uidGenerator = new CachedUidGenerator();
        uidGenerator.setWorkerIdAssigner(disposableWorkerIdAssigner);
        uidGenerator.setTimeBits(29);
        uidGenerator.setWorkerBits(21);
        uidGenerator.setSeqBits(13);
        uidGenerator.setEpochStr("2016-09-20");
        uidGenerator.setBoostPower(3);
        uidGenerator.setPaddingFactor(50);
        uidGenerator.setScheduleInterval(60);
        // uidGenerator.setRejectedPutBufferHandler(...); // 根据需求设置拒绝策略
        // uidGenerator.setRejectedTakeBufferHandler(...); // 根据需求设置拒绝策略
        return uidGenerator;
    }

    @Bean
    public DisposableWorkerIdAssigner disposableWorkerIdAssigner() {
        return new DisposableWorkerIdAssigner();
    }
}

defaultUidGenerator:基于模式雪花算法,发生时钟回拨直接抛异常 cachedUidGenerator:解决了时钟回拨问题,解决方法如下:

  • 自增列:UidGenerator的workerId在实例每次重启时初始化,且就是数据库的自增ID,从而完美的实现每个实例获取到的workerId不会有任何冲突。
  • RingBuffer:UidGenerator不再在每次取ID时都实时计算分布式ID,而是利用RingBuffer数据结构预先生成若干个分布式ID并保存。
  • 时间递增 :传统的雪花算法实现都是通过System.currentTimeMillis()来获取时间并与上一次时间进行比较,这样的实现严重依赖服务器的时间。而UidGenerator的时间类型是AtomicLong,且通过incrementAndGet()方法获取下一次的时间,从而脱离了对服务器时间的依赖,也就不会有时钟回拨的问题

单侧用例如下:

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PointApplication.class)
@Slf4j
public class CachedUidGeneratorTest {

    @Resource(name = "defaultUidGenerator")
    private UidGenerator defaultUidGenerator;

    @Resource(name = "cachedUidGenerator")
    private UidGenerator cachedUidGenerator;

    @Test
    public void testSerialGenerate01() {
        // Generate UID
        long uid = defaultUidGenerator.getUID();

        // Parse UID into [Timestamp, WorkerId, Sequence]
        // {"UID":"180363646902239241","parsed":{    "timestamp":"2017-01-19 12:15:46",    "workerId":"4",    "sequence":"9"        }}
        System.out.println(defaultUidGenerator.parseUID(uid));
    }

    @Test
    public void testSerialGenerate02() {
        // Generate UID
        long uid = cachedUidGenerator.getUID();

        // Parse UID into [Timestamp, WorkerId, Sequence]
        // {"UID":"180363646902239241","parsed":{    "timestamp":"2017-01-19 12:15:46",    "workerId":"4",    "sequence":"9"        }}
        System.out.println(cachedUidGenerator.parseUID(uid));
    }
}

执行结果

txt 复制代码
{"UID":"3942331703401390080","timestamp":"2023-12-28 22:45:07","workerId":"1","sequence":"0"}

针对CachedUidGenerator详细介绍可以参考官网说明,这里不在详细阐述。

参考:
uid-generator
UidGenerator:百度开源的分布式ID服务(解决了时钟回拨问题)

相关推荐
SimonKing1 分钟前
分享一款可以管理本地端口的IDEA插件:Port Manager
java·后端·程序员
索荣荣7 分钟前
Maven配置文件(pom.xml)终极指南
java·开发语言
代码栈上的思考21 分钟前
SpringBoot 拦截器
java·spring boot·spring
送秋三十五24 分钟前
一次大文件处理性能优化实录————Java 优化过程
java·开发语言·性能优化
龙山云仓26 分钟前
MES系统超融合架构
大数据·数据库·人工智能·sql·机器学习·架构·全文检索
雨中飘荡的记忆27 分钟前
千万级数据秒级对账!银行日终批处理对账系统从理论到实战
java
jbtianci32 分钟前
Spring Boot管理用户数据
java·spring boot·后端
Sylvia-girl35 分钟前
线程池~~
java·开发语言
未来龙皇小蓝37 分钟前
RBAC前端架构-02:集成Vue Router、Vuex和Axios实现基本认证实现
前端·vue.js·架构
魔力军40 分钟前
Rust学习Day3: 3个小demo实现
java·学习·rust