Giter VIP home page Giter VIP logo

uid-generator's Issues

Invalid default value for 'CREATED'

When Step 2: Create table WORKER_NODE, Invalid default value for 'CREATED' issures

CURRENT_TIMESTAMP is only acceptable on TIMESTAMP fields. DATETIME fields must be left either with a null default value, or no default value at all - default values must be a constant value, not the result of an expression.

relevant docs:
http://dev.mysql.com/doc/refman/5.0/en/data-type-defaults.html

https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp

改造成了uid-generator-spring-boot-starter

项目地址 https://github.com/wujun234/uid-generator-spring-boot-starter ,已发布到maven**仓库。

基于 百度UidGenerator, 做了以下改动:

  • 改造为spring-boot-starter的形式,不用部署为分布式,直接建表、在项目中引入,即可使用
  • 针对时钟回拨,提供了修正选项(默认启用,可通过配置关闭),小于阈值直接休眠,大于阈值更改机器号
  • 对机器id用尽提供了复用策略:取余
  • 解除id位数限制,由“必须64位”改为“不大于64位”,可根据需要获取更短id

Quick Start

1 引入maven依赖

<dependency>
    <groupId>com.github.wujun234</groupId>
    <artifactId>uid-generator-spring-boot-starter</artifactId>
    <version>1.0.2.RELEASE</version>
</dependency>

2 建表

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: CONTAINER(1), ACTUAL(2), FAKE(3)',
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;

3 开始使用

//@Resource
//private UidGenerator defaultUidGenerator;

@Resource
private UidGenerator cachedUidGenerator;

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

    // Parse UID into [Timestamp, WorkerId, Sequence]
    // {"UID":"450795408770","timestamp":"2019-02-20 14:55:39","workerId":"27","sequence":"2"}
    System.out.println(cachedUidGenerator.parseUID(uid));

}

双Buffer使用问题

在服务器启动时,类BufferPaddingExecutor构造函数对lastSecond进行初始化赋值,之后不再采用系统时间尽心校准,进而避免了时钟回调问题。我不太理解为什么要采用双buffer设计,既然lastSecond是稳定的lastSecond.incrementAndGet()进行递增,在每秒内的sequence(默认seqBits为13)也可以在0和8191间递增,那么此时采用双Buffer的意义是什么呢?

关于UID可支持时长问题

delta seconds (28 bits) 当前时间,相对于时间基点"2016-05-20"的增量值,单位:秒,最多可支持约8.7年
怎么计算得到的8.7年

生成ID全部是负数

uidGenerator-timeBits: 23
uidGenerator-workerBits: 31
uidGenerator-seqBits: 9
uidGenerator-epochStr: '2018-12-22'
uidGenerator-boostPower: 3
uidGenerator-paddingFactor: 50
uidGenerator-scheduleInterval: 60
当参数为这些时会出现负数

CachedUidGenerator尽可能地保持时间最新

目前CachedUidGenerator在初始化的时候lastSecond用的是当前秒,后续的只是在此基础上+1(不在依赖时钟,并发高的时候还可以预借),不过如果ID生成很少,那么就可能导致生成的ID(解析后)于实际的时间相差很多。
虽然ID相对有序,但是有些时候还是需要根据ID大致知道相应的时间。目前这样ID里面的时间就不需要很准确。
比如在schedule周期性调整lastSecond,或者其他方法。

集群情况下怎么部署呢

看了看代码 网上也找了找资料, 愣是没找到集群的使用方法. 集群下可以不重复 并且有序吗

Flag-RingBuffer是否有必要存在

既然有tail、cursor分别表示生产,消费的offset,直接根据这个两个变量确定put, take的位置不就好了吗?为什么还需要Flag-RingBuffer,以及判断里面相应位置的状态?

是否可以考虑另一种workerid的分配方案

默认实现的是重启拿取新的workerid的方式,我觉得这块可以优化。可以基于唯一不变性约束来减少workerid的消耗,比如拿机器mac+机器ip地址作为唯一的方式来确定workerid,通过mysql insert on duplicate update模式,如果该唯一的标识(mac+ip)不存在则插入成功,得到新的workerid,如果已经存在,则获取原有的id信息,这样可以减少workerid的消耗,那么workerid的位数就可以不需要那么多位,比如可能只需要15bit,就可以实现32768台机器的部署规模,将更多的位数留给timeBits和seqBits,提高使用年限和并发度,同时可以引入heartbeat心跳,对于一些下架的机器根据失效的心跳从worker表中踢除,以节约更多的机器。

以上是我个人的见解,并根据以上思路实现了一个ReusableWorkerIdAssigner,如有不对的地方,请指正。如果可以,我也可以提一个PR。

RingBuffer内slots数组的补齐问题

感谢百度开源了本项目,鄙人在研读源码的时候有点疑惑,希望能得到各位前辈的指点。

RingBuffer内有两个环形数组,一个名为slots作用为存储UID,另一个名为flags用于存储标志位。

具体引用代码如下

https://github.com/baidu/uid-generator/blob/master/src/main/java/com/baidu/fsg/uid/buffer/RingBuffer.java#L51-L52

slots中的元素唯一被填充的填充的地方是com.baidu.fsg.uid.buffer.RingBuffer#put,并通过线程池间接调用了该方法,多线程间对slots进行写操作在多核处理器下应该也会使得该数组发生伪共享问题,请问为什么不像flags一样地去使用PaddedAtomicLong进行补齐,或者是我对伪共享有错误的理解,望不吝赐教!

Performance for the DefaultUidGenerator

It is time-consuming for currentTimeMillis ===> seconds
long currentSecond = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
under method getCurrentSecond().

After i change to currentTimeMillis instead of transferring to seconds,
It is much faster. Otherwise DefaultUidGenerator is very slow.

DefaultUidGenerator的性能问题

对比了原生的snowflake和DefaultUidGenerator的性能,在同样的测试环境下:

  • 测试机器:mac book pro i7 6核
  • 测试数据量:10万
  • 关闭所有日志和输出
  • 使用串行生成id的方法

消耗时间对比:

  • snowflake:37ms
  • DefaultUidGenerator: 23692ms

是什么导致两者差距这么明显呢?分析代码后,怀疑是DefaultUidGenerator的getNextSecond()方法阻塞了请求导致性能的急剧下降。因为DefaultUidGenerator是按秒为单位去阻塞的,而原生的snowflake是按毫秒阻塞的。对比两者的阻塞次数:

  • snowflake:16次(毫秒级别)
  • DefaultUidGenerator: 24次(秒级别)

从中分析可以得知,DefaultUidGenerator的getNextSecond()方法确实是造成性能急剧下降的罪魁祸首,因为在10万次id生成的过程中,阻塞了24次,尽管每次不一定到1秒(取决于该秒内序列号被使用完时距离1秒结束的时间差),但是结论应该是显而易见的。

为了验证这个结论,我将DefaultUidGenerator改成毫秒级别,重新测试DefaultUidGenerator.testSerialGenerate(),消耗时间降到了:38ms。

所以,毫秒级序列号的设计比秒级序列号设计性能要高得多。

设计不合理

image
使用的是固定大小线程池,可是给线程池提交的任务的时候又特意控制并发,转为单线程。
image
我觉得没必要这样设置,直接使用一个线程就行了,或者不要控制并发,提高生成速度。

这个是怎么算的?例如节点采取用完即弃的WorkerIdAssigner策略, 重启频率为12次/天, 那么配置成{"workerBits":23,"timeBits":31,"seqBits":9}时, 可支持28个节点以整体并发量14400 UID/s的速度持续运行68年. 时间 2的31次方-1 / 86400 / 365 = 68年

序号 2的9次方 = 512

workid数 2的23次方 = 8388608

重启次数 68 乘上 365 乘上 12 天 = 297840

应用数 节点 除以 重启次数 = 8388608 / 297840 = 28

并发 28 乘上 512 = 14336

这个算的思路是对的吗?

请将JAR发布到官方仓库

因我不具有groupid:com.baidu. 的deploy角色,请将本项目的jar部署到官方仓库,造福广大javaer,感谢!

CachedUidGenerator can provide 6000 thousand/s throughput, how can it happen?

我的测试数据如下:

JMH 1.17.5 (released 249 days ago, please consider updating!)
VM version: JDK 1.8.0_101, VM 25.101-b13
VM invoker: D:\apps\jdk\jdk1.8.0_101\jre\bin\java.exe
VM options: -Didea.launcher.port=7537 -Didea.launcher.bin.path=D:\apps\JetBrains\IntelliJ IDEA 2016.3.1\bin -Dfile.encoding=UTF-8
Warmup: 5 iterations, 5 s each
Measurement: 20 iterations, 5 s each
Timeout: 10 min per iteration
Threads: 20 threads, will synchronize iterations
Benchmark mode: Throughput, ops/time
Benchmark: com.vpal.beifu.customs.benchmarktest.IdGeneratorBenchmarkTest.benchPrecondition

Run progress: 0.00% complete, ETA 00:02:05
Fork: 1 of 1
Warmup Iteration

Warmup Iteration 2: 91892.284 ops/s
Warmup Iteration 3: 91975.161 ops/s
Warmup Iteration 4: 92341.250 ops/s
Warmup Iteration 5: 99186.817 ops/s
Iteration 1: 90499.371 ops/s
Iteration 2: 86129.521 ops/s
Iteration 3: 75192.984 ops/s
Iteration 4: 78517.215 ops/s
Iteration 5: 78064.527 ops/s
Iteration 6: 78340.945 ops/s
Iteration 7: 75036.587 ops/s
Iteration 8: 77089.315 ops/s
Iteration 9: 78202.288 ops/s
Iteration 10: 75584.124 ops/s
Iteration 11: 74410.960 ops/s
Iteration 12: 76744.887 ops/s
Iteration 13: 74803.554 ops/s
Iteration 14: 75628.749 ops/s
Iteration 15: 74925.918 ops/s
Iteration 16: 76541.028 ops/s
Iteration 17: 77475.313 ops/s
Iteration 18: 75880.433 ops/s
Iteration 19: 77597.357 ops/s
Iteration 20: 75894.316 ops/s

Result "com.vpal.beifu.customs.benchmarktest.IdGeneratorBenchmarkTest.benchPrecondition":
77627.970 ±(99.9%) 3419.012 ops/s [Average]
(min, avg, max) = (74410.960, 77627.970, 90499.371), stdev = 3937.339
CI (99.9%): [74208.958, 81046.981] (assumes normal distribution)

Run complete. Total time: 00:02:09

Benchmark Mode Cnt Score Error Units
IdGeneratorBenchmarkTest.benchPrecondition thrpt 20 77627.970 ± 3419.012 ops/s

是否有必要使用双buffer

本身id的生成是纯内存操作,靠移位就可以完成。buffer的意义个人感觉不大,而且put和take的时候做很多异步操作更是对cpu的一种浪费。

求问CachedUidGenerator的意义何在呢??

getId获取时间比较长

@baozhi 咨询个性能问题啊
本地压测通过http获取id时,有几次耗时超过300ms(还有超过1s的),这个不是在内存连续存储的么,怎么还这么慢?
是一遍获取id,一点存储id导致?
配置:
CachedUidGenerator、boostPower=7
-Xms12G -Xmx12G
server.tomcat.max-threads=1000

   jemeter 500线程,吞吐量9K左右 

id 重复问题

UidGenerator 生成id 重复的问题
在对部署在多台docker里面的项目进行压测的时候,使用UidGenerator 省的的id 会出现重复的问题,网上找了,好久 好像大家都没有这方面的问题,为啥就我会出现呢 ~~~~
请问有其他人遇到过吗? 是怎么解决的

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.