暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

分布式ID生成算法之SnowFlake

码农的修炼之道 2019-02-16
226

      在分布式场景下,比如订单系统,订单的ID需要我们使用分布式ID生成策略。

    常见的分布式ID生成方法包括:

(1)UUID,UUID是JDK自带的一种ID生成方式,它是一种不重复的随机ID生成方案,但是这种ID不是自增的。

(2)Redis方式,每次需要生成一个ID,就去Redis获取一下。 

(3)Oracle数据库。Oracle中有一种与表无关的数据库对象,可以获取自增ID。

(4)SnowFlake算法。这是程序自己实现的一种ID生成算法,也是一种开源的方案。

      SnowFlake算法来源于Twitter的开源代码,可以去这个地址下载

(https://github.com/twitter-archive/snowflake)。


     下面,我们来看看SnowFlake算法原理。

SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图:

1位
,不用。二进制中最高位为1的都是负数,但是我们生成的id一般都使用整数,所以这个最高位固定是0

41位
,用来记录时间戳(毫秒)。

10位
,用来记录工作机器id。注意,这里有5位表示datecenterId,另外5位表示workerId。也就是可以区分不同的机器产生的ID都是不同的。datecenterId可以想象成一台机器上的若干数据库(订单要写入数据库)。

12位
,序列号,用来记录同毫秒内产生的不同id。 

       由于在Java中64bit的整数是long类型,所以在Java中SnowFlake算法生成的id就是long来存储的。

     SnowFlake可以保证所有生成的id按时间趋势递增、整个分布式系统内不会产生重复id(因为有datacenterId和workerId来做区分)

使用案例

      下面我们来看看怎么使用这个算法,这里我直接使用开源的算法实现类IdWorker。

输出结果为:

   这里设定的workerId=0,datacenterId=0,sequence=0。可以看出,可以参数自增的ID号。

有趣的源码

     首先看一下构造函数,构造函数的目的在于初始化workerId,datacenterId,sequence等参数,并进行校验。

      我们看到,上面代码里面有个判断workID是否大于maxWorkerID。

     这里的maxWorkerID是通过位操作来计算5bit的workerID能表示的最大数 。计算如下:

     在nextID方法中,有个sequence=(sequence+1)&sequence。

private long sequenceMask = -1L ^ (-1L << sequenceBits);

     这里sequenceMask表示的是12bit表示的最大数,也就是4095.上面的代码目的是防止溢出。

    我们可以演示一下:

       此外,nextID方法中最后返回结果的时候,还是位运算,目的是将4个数据合成一个数据(即定义的数据结构)输出,可以自己动手试试看。

总结

     总的来说,代码其实不复杂。但是代码里面蕴含了许多有趣的技巧,我们可以学习一下。在理解了这个算法之后,其实还有一些扩展的事情可以做:

1、根据自己业务修改每个位段存储的信息。算法是通用的,可以根据自己需求适当调整每段的大小以及存储的信息。

2、解密id,由于id的每段都保存了特定的信息,所以拿到一个id,应该可以尝试反推出原始的每个段的信息。反推出的信息可以帮助我们分析。比如作为订单,可以知道该订单的生成日期,负责处理的数据中心等等。

文章转载自码农的修炼之道,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论