关于分布式uuid的一点设想

时间:2021-03-09 12:42:14

在一次公开课上,听别人讲过全局分布式uuid的设计,听过twitter的snowflake的设计。也听过,如果使用单独的计数器服务,不可能每次都保存当前计数器到文本,自己想到应该可以每隔一些数,例如1万次,10万次,反正64位的空间比较大,然后保存起来,那么就没有每次保存,对硬盘的写入压力。当出现故障的时候,跳过这么多数就可以了。只是,这个据说也是微信的uuid设计方案。我如果拿这个来讲,岂不是抄袭很严重。下面先给出uuid服务的图:

关于分布式uuid的一点设想

对于snowflake和微信的计数保存方案,各个各的优点,在我看来,可以考虑联合这两种方案。方案大致如下:

1. 对于不同的uuid server,给予不同的前缀,那么不同的uuid服务器的计数互不干扰,这个依赖于运维来实现,当然,如果开发打算自己做,我觉得也没有问题。

2. 对于每个uuid服务器,启动的时候,从当前电脑获取出当前的时间,在这里,可以考虑从epoch开始的秒数,然后有一个次数计数器,从0开始计数,假设这个计数器为32位,每次来一个请求,次数计数器+1,可以考虑当这个计数器达到最大值时,检查一下当前秒数,是不是和上一次获取秒数不同,如果相同(对于当前例子来说,基本不可能),则休眠若干毫秒,然后再次获取当前时间,如果不同,则将次数计数器重新置为0,继续进行计数。你们应该不难看出,我这里的计数器是

  

uuid server 的前缀 + 当前秒数 + 次数计数器值

3. 对于程序崩溃的处理,并不困难,只需要在程序启动的地方添加一个sleep(休眠),让程序1s后启动就可以了,那么就可以保证新的计数器,之前不会出现过。

4. 参数说明:上面所说的参数,可能不是最优的,例如,可以考虑,将时间改为从epoch开始的毫秒数,0.1s,或者其他的,这个看各自的需求,计数器,也可以考虑使用30个bit,24个bit,那么对应的最大值也需要根据这个进行调整,可能使总的计数器位数更少,或者更多。总之,这些东西有赖于各自测试,达到自己的目的就好。调整时间的精度,例如精度调整为0.1s,那么程序再次启动,sleep的时间可以更短,这个对某些项目可能有所帮助。

5. 需要处理的问题,这里主要是时间。有一点需要说明,这种实现方式,不同电脑存储的uuid之间没有严格的时间关系,没办法比较先后,同一台电脑的uuid可以区分先后。对于同一台电脑,重启之后运行正常,依赖于本地时间没有被改动,如果本地时间向前调整,那么可能会出现uuid重复的问题,先后顺序也不能被保证。只是,如果次数计数器值空间很大,那么出现重复的几率应该不大。如果打算将当前的前缀给另外一台电脑使用,那么需要那台电脑的时间不低于当前电脑,否则也可能出现重复。只是,如果那台电脑的时间在这台电脑时间之后,应该没有问题。

6. 如果担心时间问题,可以在程序中改变时间的时候,进行一次打印,打印出当前的时间,再次启动的时候可以进行查看。

7. 顺序的额外说明:不同电脑的时间同步对uuid跨电脑排序用处不大,因为uuid中的时间,只能说明记录的uuid发生在这个时间或者之后,并不一定是这个时间。

8. 对于计数器的实现来说,可以考虑使用atomic,也可以考虑使用中间件一类的,看实现方便,以及各自喜好。

9. 如果次数计数器为64位,或者类似的长度,可以考虑不进行检查,只在每次重启的时候,sleep例如1s,然后从0开始计数,因为按照当前甚至未来一段时间,64位的数字也很难在比较短的时间内耗尽,这样可以使处理逻辑更加简单,不过,可能会增加总的计数器的位数.

关于这个问题,就简单说到这里,如果文中有什么问题,希望能够指出来。