Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

若机器时间回拨或者前置,如何保证序列号的唯一?有什么机制能保证本地时间变动不影响服务正常工作? #35

Closed
jieniyimiao opened this issue Jun 4, 2019 · 6 comments

Comments

@jieniyimiao
Copy link

No description provided.

@huaweihomelq
Copy link

百度的id生成器是这样来处理的,每次的启动都在mysql里记录一个值,下次启动舍弃重新生成这个值,貌似支持百万级别以上的重复启动

@jieniyimiao
Copy link
Author

@huaweihomelq 多谢回复。 是的,这个是机器工作节点ID的生成策略(用完即弃),但是我说的是另外一种情况:序列号生成服务正常运行,但是服务器的本地时间发生了变化,比如前移了几秒,那么服务好像就不能正常工作了,会发生序列号重复的情况。 因为服务强依赖本地时间。 这种场景有没有考虑,问题该如何解决?

@huaweihomelq
Copy link

你所说的时钟回调,在应用介绍里貌似不是已经解决了吗?难道你现在使用的版本,已经存在并发生服务不可用的情况了?如果有的话,希望能了解下情况,我目前做的项目,都准备上线使用了,感觉有点恐怖

@jieniyimiao
Copy link
Author

jieniyimiao commented Jun 5, 2019

我现在没遇到这种情况,不过这种情况肯定是存在的。源代码中时钟不对直接抛出异常,并没有做其他额外的处理,比如如果时钟回调时间比较小,可以等待;或者增加冗余位进一处理等。
我目前还处于开发预研阶段、还没上线,因为只要使用snowflake机制必然会出现这个问题,所以我这边有些担忧。
我们项目中准备把mybatis替换为JPA,Spirng 替换为Spring boot , 然后在做一些适应性的改造。

或者可以考虑:如果时钟回调则强制刷新到最新,这样应该就没这个问题了。这样就不会重复了。

源码如下:
// Clock moved backwards, refuse to generate uid
if (currentSecond < lastSecond) {
long refusedSeconds = lastSecond - currentSecond;
throw new UidGenerateException("Clock moved backwards. Refusing for %d seconds", refusedSeconds);
}

@huaweihomelq
Copy link

通过上面对UidGenerator的分析可知,CachedUidGenerator方式主要通过采取如下一些措施和方案规避了时钟回拨问题和增强唯一性:

自增列:UidGenerator的workerId在实例每次重启时初始化,且就是数据库的自增ID,从而完美的实现每个实例获取到的workerId不会有任何冲突。

RingBuffer:UidGenerator不再在每次取ID时都实时计算分布式ID,而是利用RingBuffer数据结构预先生成若干个分布式ID并保存。

时间递增:传统的雪花算法实现都是通过System.currentTimeMillis()来获取时间并与上一次时间进行比较,这样的实现严重依赖服务器的时间。而UidGenerator的时间类型是AtomicLong,且通过incrementAndGet()方法获取下一次的时间,从而脱离了对服务器时间的依赖,也就不会有时钟回拨的问题(这种做法也有一个小问题,即分布式ID中的时间信息可能并不是这个ID真正产生的时间点,例如:获取的某分布式ID的值为3200169789968523265,它的反解析结果为{"timestamp":"2019-05-02 23:26:39","workerId":"21","sequence":"1"},但是这个ID可能并不是在"2019-05-02 23:26:39"这个时间产生的)。

参考:https://www.jianshu.com/p/5509cc1b9a94

@Timyooo
Copy link

Timyooo commented Jun 6, 2019

第一种情况:实例关闭,时间回拨,重启实例。
这种情况下,实例每次重启都分配一个新的workId,保证了重启后生成的id与之前产生的id不会重。
第二种情况:实例一直运行,时间回拨。
DefautlUidGenerator使用了System.currentTimeMillis()获取时间与上一次时间比较,可能会有currentTime<lastTime情况发生,抛错错误。
而CachedUidGenerator使用 AtomicLong的incrementAndGet()来获取下一次时间,脱离了时间,所以可以正常使用。

@baozhi baozhi closed this as completed Jun 10, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants