分布式系统全局唯一ID的几种实现方式

现如今可谓是微服务、分布式、IoT(物联网)横行的时代,作为一名开发者始终还是要保持一定的危机意识,特别是在日常的项目开发中,若是有机会接触到一些关于微服务、分布式下的应用场景,应当硬着头皮、排除万难,主动应承下来上去大干一场;这期间不管结果如何,积累下来的经验将会让自己受益匪浅;而本文要介绍的“分布式全局唯一ID”便是一种典型的分布式应用场景!!!
话不多说,咱们直接进入正题~~~
说起这个全局唯一ID,你可能会第一时间想到“数据库的自增主键”、“UUID”、“雪花算法”等等,更有甚者,还能说出一些大厂开源的组件,比如滴滴的IDWorker、美团的Leaf等等,没错,这些确实是可以实现全局唯一ID的方案,你能想到这些点,那涉猎其实还是挺广的 。
而对于“全局唯一ID/编号/编码”的应用场景,在现实生活中还是比较多的,比如电商平台中“订单系统”的订单编号,“进销存系统”中的商品编号、订单编号,“支付”过程中订单流水号等等;接下来debug将会总结性的介绍下目前市面上比较流行的“全局唯一ID”的几种实现方式,并针对分布式场景下的几种实现方式进行代码实战 。
话不多说,直接进入正题,先贴张思维导图吧,总结性地概括下目前网上比较流行的几种方式(当然啦,图片来源于互联网哈,因为debug懒得去制作了!)

分布式系统全局唯一ID的几种实现方式

文章插图
 
▲点击查看大图(若图片不清晰可点击文章底部阅读原文)
结合上图几种方式,debug再概括性的介绍下吧:
1、 数据库的自增主键简介:这一点相信写过代码的小伙伴都晓得,主要利用主键ID的auo_increment特性,每进来一条数据时数据库自动为其生成当前最大的ID并作为该条记录的主键;
优点:简单、便捷;
缺点:只能限于单机,严重依赖于DB,仅可限于DB相关的业务,可用性还是有点差;
 2、批量预生成ID简介:DB只存储当前最大的ID值,每次需要ID时,则按照顺序批量生成N个有序的ID列表,并将最大的ID值 + N 。
优点:相对于第一种方式性能还是提高了不少;
缺点:只能限于单机,还是仍然得依赖于DB,可用性还是有点差;而且批量生成的ID可能断层(比如服务挂了然后重启就可能跳过部分ID,如果服务有多个,将难以保证其有序性)
3、 UUID的方式
简介:通用唯一识别码,这个估计众所周知啦,不作过多的介绍了!
优点:简单,直接 UUID.randomUUID().toString()即可搞定;
缺点:比较长、占用空间大;无序且不利于索引,在实际项目中不建议使用;特别是在插入数据库时如果用UUID生成的ID作为主键的话,很可能会引起B+树的不断重平衡;
4、基于时间戳简介:比如按照规则:yyyyMMdd+ N位随机数 或者 yyyyMMddHHmmss + N位随机数 。
优点:可行,而且生成的ID编号前半段有序,有一定的业务意义;
缺点:当并发产生的数据量比较大时,那N位随机数会出现重复的可能(虽然可以通过各种方式去重,比如redis的Set,但代价还是相当高的,因为得不断的 while判断是否重复…)
5、SnowFlake算法简介:Twitter开源的一种分布式ID生成算法,结果是一个Long型的64位的ID;其核心思想是将64位划分为各个段,其中0号位不用,连续41位表示时间戳,连续10位表示工作机器ID,最后12位则表示毫秒级别的序列号,如下图所示:
分布式系统全局唯一ID的几种实现方式

文章插图
 
【分布式系统全局唯一ID的几种实现方式】
优点:可以说是分布式场景下生成全局唯一ID的一种经典算法吧,采用JAVA生成,对于咱们Java的小伙伴来说可以说是相当接地气的了;
缺点:目前倒没发现有啥缺陷,如果硬要说有,那就是“时钟回播”的问题了,但其实没啥事的话别乱重置系统时钟或者乱调系统时钟则一般是没啥问题的!如果还说它仍然有缺点的话,那就是它的算法实现逻辑,即nextId()方法里面的代码还真的挺复杂,一堆位运算 理解起来确实比较消耗脑细胞(除此之外,那就是它最终生成的ID长度有点长啦)!
6、 原子操作类AtomlcXX简介:JUC包下经典的原子操作类,可以基于它生成自增、有序且全局唯一的编号
优点:底层采用CAS(Compare And Swap)机制实现,并发场景下可以保证“自增”代码逻辑的安全性;


推荐阅读