id

[Build Status]Build Status 1 [Quality Gate]Quality Gate 1 [Coverage Status]Coverage Status 1 [Maven Central]Maven Central 1 [License: MIT]License_ MIT_License_ MIT

id generator with time backward-compatible.

usage

import com.github.gobars.Id;

long bizID = Id.next();
worker_id

Spring 配置:

@Configuration
public class SpringAppConfig {
  @Bean
  public DataSource getDataSource() {
    val dataSource = new DruidDataSource();
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/id?useSSL=false&zeroDateTimeBehavior=convertToNull&useUnicode=yes&autoReconnect=true&characterEncoding=UTF-8&characterSetResults=UTF-8");
    dataSource.setUsername("root");
    dataSource.setPassword("root");

    return dataSource;
  }

  @Bean
  public IdNext idNext(@Autowired DataSource dataSource) {
    val connGetter = new ConnGetter.DsConnGetter(dataSource);
    val workerIdDb = new WorkerIdDb().table("worker_id").connGetter(connGetter).biz("default");
    val spec = "epoch=20200603,timestampBits=41,backwardBits=0,workerBits=10,seqBits=12,roundMs=1";
    return new SnowflakeDb(Conf.fromSpec(spec), workerIdDb);
  }
}

Spec说明:

| Spec               | 值格式               | 默认值      | 说明                                                                                       |
| ------------------ | ----------------- | -------- | ---------------------------------------------------------------------------------------- |
| epoch              | yyyyMMdd          | 20200603 | 服务器第一次上线时间点                                                                              |
| timestampBits      | 整型                | 41       | 时间戳占用比特位数                                                                                |
| roundMs            | 整型                | 1        | 时间戳规整到的时间单位(毫秒)                                                                          |
| backwardBits       | 整型                | 0        | 时间回拨序号占用比特位数                                                                             |
| workerBits         | 整型                | 10       | worker占用比特位数                                                                             |
| seqBits            | 整型                | 12       | 自增序号占用比特位数                                                                               |
| maxBackwardSleepMs | 整型                | 1000     | 最大时间回拨                                                                                   |
| timestampBy        | cache/system/nano | cache    | 时间戳计算算法 cache: SystemClock.now(), system: System.currentMillis(),nano: System.nanoTime() |

使用:

@Bean
public class XXService{
  @Autowired IdNext id;

  public void business() {
    // ...
    long bizID = id.next();
    // ...
  }
}

WorkerId 获取顺序

WORKERID

原理

snowflake 改进:

|     API     | sign  | timestamp  | backwardId | workerId |  sequence  |         max          |  limit  |            years             | remark                              |
|:-----------:|:-----:|:----------:|:----------:|:--------:|:----------:|:--------------------:|:-------:|:----------------------------:|:----------------------------------- |
|      -      |  符号位  |    时间戳     |   时间回拨序号   |  工作机器ID  | 同时间戳内产生的序列 |         最大值          |   限制    |             使用年限             | 备注                                  |
|  Id.next()  | 1 bit | 41 bit(ms) |   2 bit    |  8 bit   |   12 bit   |         2^63         | 4096/ms | 2^41/1000/60/60/24/365.5≈69年 | 标准snowflake中10位workerId抽出2位作为时间回拨序号 |
| Id12.next() | 1 bit | 29 bit (s) |   1 bit    |  3 bit   |   6 bit    | 2^39=549,755,813,888 |  64/s   |   2^29/60/60/24/365.5 ≈17年   | 产生最大12位长度数字的ID                      |

时间回拨问题解决方案

最新当前时间上次获取的时间最新当前时间上次获取的时间~/.worker.backwardId.108108

不能解决以下极端条件:

  1. 程序启动后,停机时间回拨(程序无法感知)
  2. 程序运行中,连续4次时间回拨(超过最大回拨序号)

资料

脚本

MySQL

drop table if exists worker_id;
create table worker_id
(
    id       bigint auto_increment primary key comment 'worker id',
    created  datetime default current_timestamp comment '创建时间',
    ip       varchar(60) comment '当前机器IP',
    hostname varchar(60) comment '当前机器名称',
    pid      int comment '应用程序PID',
    reason   varchar(60) comment '申请原因 start:启动 backwards:时间回拨',
    biz      varchar(60) comment '当前业务名称'
) engine = innodb
  default charset = utf8mb4 comment 'worker id 分配表';

or online format:

drop table if exists worker_id;create table worker_id(id bigint auto_increment primary key, created datetime default current_timestamp,ip varchar(60),hostname varchar(60),pid int,reason varchar(60),biz varchar(60))engine = innodb default charset = utf8mb4;

Oracle

drop table worker_id;
create table worker_id
(
    id       int primary key,
    created  timestamp default current_timestamp,
    ip       varchar2(60),
    hostname varchar2(60),
    pid      int,
    reason   varchar2(60),
    biz      varchar2(60)
);

comment on table worker_id IS 'worker id 分配表';

comment on column worker_id.id IS 'worker id';
comment on column worker_id.created IS '创建时间';
comment on column worker_id.ip IS '当前机器IP';
comment on column worker_id.hostname IS '当前机器名称';
comment on column worker_id.pid IS '应用程序PID';
comment on column worker_id.reason IS '申请原因 start:启动 backwards:时间回拨';
comment on column worker_id.biz IS '当前业务名称';

CREATE SEQUENCE worker_id_seq
    START WITH 1
    INCREMENT BY 1
    CACHE 100;

CREATE OR REPLACE TRIGGER trigger_worker_id_seq
    BEFORE INSERT
    ON worker_id
    FOR EACH ROW
BEGIN
    SELECT worker_id_seq.nextval
    INTO :new.id
    FROM dual;
END;