分布式唯一id生成器-snowflake雪花算法

Posted coding涛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式唯一id生成器-snowflake雪花算法相关的知识,希望对你有一定的参考价值。

系统开发经常要用到流水号即唯一id,用于唯一标识某一个业务或记录。常用的唯一id生成方式有如下几种:


借助数据库实现

通过数据库生成唯一id,一般通过下面两种方式:

1、通过数据库的自增特性生成,实现数据库内唯一。例如mysql、SQL Server支持自增字段,oracle提供了sequence序列。这种方式的缺点是将系统与特定数据库绑定,并且不是所有数据库都有自增特性。

2、设置专门的流水号表,表中的初始化一条记录。每次使用时,记录值增加1。这种方式的缺点是:依赖数据库的锁,不是适合并发量高的系统。


通过数据库生成流水号的好处是使用方便,也容易理解。比较适合并发量不高的单机应用,不适合高并发且多机器集群部署的系统。


利用第三方中间件特性实现

例如:通过Redis的incr和increby实现;利用MongoDB的ObjectId实现;利用Zookeeper的znode数据版本实现;

优点:性能优于数据库;可集群部署

缺点:依赖第三方中间件、增加系统复杂度


使用UUID

优点:本地生成,不依赖第三方组件、使用简单、性能好。

缺点:长度较长,Java提供UUID是36位,不利于存储,并且它是无序的,而我们一般希望流水号能按时间有序增大。


Twitter的SnowFlake雪花算法介绍

       SnowFlake最早是由Twitter公司提出的。最初Twitter需要把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务。

雪花算法使用64个比特位生成,这64个比特位分为四个部分:

第一部分:1位的最高位符号位,始终为0,不可用。 二进制中最高位为1表示负数,但是我们生成的id一般都使用正整数,所以这个最高位固定是0,用于保证生成的序号不为负数。

第二部分41位的时间序列位,精确到毫秒级,41位的长度可以使用2^41=69年。使用时,设置一个基准时间,当前时间与基准时间的毫秒差即为它的值。这样做的好处是可以保证得到的流水号随时间趋势有序增加。

第三部分10位的机器id标识位,10位的长度最多支持部署2^10=1024个节点。用于保证多个服务器节点之间不重复。

第四部分12位的序列号,序列号即为一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的序列号支持每台服务器每毫秒产生 2^12=4096 个ID序号。

 

说明

1、雪花算法的后面3个部分的长度可以根据业务系统的实际情况调整,例如,你如果觉得69年的使用时间有点短,可以增加到42位(138年),相应地缩减1位长度的机器id标识或序号。

2、雪花算法生成的流水号是趋势增加,不是固定步长地按序增加。这样做的好处是,用户或竞争对手无法根据流水号判断系统的真实业务量。

3、对于Java语言而言,64个比特位正好是Java中long类型的长度,所以在Java中雪花算法生成的流水号使用long类型保存。

 

雪花算法的实现-百度Java雪花算法uid-generator

https://github.com/baidu/uid-generator

百度雪花算法支持两种生成器用于生成流水号:DefaultUidGenerator默认生成器、CachedUidGenerator缓存生成器。其中,缓存生成器并发性能更好。


如何使用百度雪花算法

       百度雪花算法没有打包成jar发布,所以我们无法使用Maven或Gradle构建工具直接引用,需要手工在github下载代码自行打包。

       在百度雪花算法中,要求创建WORKER_NODE表,这个表的目的是用于保证多服务器条件下算法中的机器id唯一。保证唯一的方式是,在每次机器重启时,百度雪花算法会自动生成一个新机器id,并通过WORKER_NODE表记录当前已经使用过的workid。

个人认为这种通过增加表保证机器id唯一的方式对于这个算法项目而言过于冗余,让算法产生过多依赖,不便于算法的使用实际项目中,可以通过其他手段保证集群下的各个机器id唯一。所以,如果你不需要使用百度雪花算法中的控制机器id唯一的功能,就可以去掉如下相关代码:

com.baidu.fsg.uid.worker.DisposableWorkerIdAssigner

com.baidu.fsg.uid.worker.dao.WorkerNodeDAO

com.baidu.fsg.uid.worker.entity.WorkerNodeEntity


流水号生成示例:

分布式唯一id生成器-snowflake雪花算法


 与Spring Data JPA整合

       一般情况下,我们设置JPA实体的唯一键时,若按下面这种方式配置,则JPA会自动创建一个流水号表hibernate_sequence。

分布式唯一id生成器-snowflake雪花算法

如果采用雪花算法的话,则应按如下方式配置:

分布式唯一id生成器-snowflake雪花算法

其中,generator(id生成器的名称)必须保证唯一,即不同实体之间,不能重复。Strategy对应的类为自定义的流水号生成类,该类必须继承IncrementGenerator类

特别注意:自定义的在这个类中,获取雪花算法生成器Bean时,不能采用spring注入的方式。注入方式获取到的bean为null,因为spring容器启动时,是先扫描JPA实体类。这里,采用的是通过ApplicationContext在运行时获取。

最后,在Spring配置类中配置雪花算法Bean

这个例子中的workid(机器id)是配置文件中的一个固定的属性值,我这里的系统是单机运行。各位可以根据项目实际情况使用自己的方式设置机器id值。若在多服务器下运行,应注意保证机器id值唯一。


以上是关于分布式唯一id生成器-snowflake雪花算法的主要内容,如果未能解决你的问题,请参考以下文章

ID生成算法-雪花算法(SnowFlake)及代码实现

springboot 分布式全局唯一id的生成-雪花算法snowflake

springboot 分布式全局唯一id的生成-雪花算法snowflake

Java中SnowFlake 雪花算法生成全局唯一id中的问题,时间不连续全为偶数解决

分布式场景全局唯一ID生成工具类(非雪花算法)

分布式场景全局唯一ID生成工具类(非雪花算法)