简单聊聊分布式ID的几种生成方案

为什么会有分布式ID?

在分布式ID中的分布式主要强调的是数据在存储时对于库或者表的分布式,也就是常说到的分库分表,如果在一张表中可以轻松的通过主键自增或者唯一索引等方式实现某个字段的唯一性,那么假设把一张表拆分为多张表,又如何保证多张表中的同一个字段的唯一性呢?这就是分布式ID要解决的问题。

对于分布式ID的一些要求

(1)唯一性:这里指的当然是全局唯一性,毫无疑问这就是分布式ID必须要做到的。
(2)有序性:ID字段上我们一般都会加索引,那么有序性就显得非常重要,能够保证插入、删除等操作的效率。
(3)长度:建立索引的字段长度,尽量越短越好,这样一次I/O,加载到内存中的索引值便能多一些。
(4)性能:能够应用到分布式ID的场景,那么多数情况下会面临的高并发的问题,也就要求分布式ID生成时要快
(5)安全:生成出来的ID要能隐藏业务中的一些信息,比如根据ID推算出订单的数量,用户的数量等等。
(6)可靠:能够稳定不间断的保证服务的可用性,也是对于分布式ID的要求。

分布式ID的生成策略

1、数据库自增ID

这是一种非常简单的方式,专门准备一张表,然后建立一个主键ID,并设置为自增列,每次需要分布式ID时,从这张表中随便插入一条数据,返回的主键ID便是分布式ID。

优点:

除了简单,也想不到有什么其他优点了。

缺点:

(1)数据库的并发能力太差,所有的请求全都压在一个数据库上。
(2)数据库单点故障问题,造成可靠性不高。
(3)ID自增,规律明显,存在数据安全隐私问题。

当然对于并发能力和单点故障问题,你可能会想到分库分表的方案,并为每个表中的自增列设置不同的步长来解决ID唯一性的问题,如下图

在这里插入图片描述
这样的方案首先就是成本问题,通过无限的添加硬件设备来解决一个分布式ID问题,明显不划算,其次便是扩容性问题,一旦流量再次达到性能瓶颈,你该如何扩容呢?

所以对于数据库自增ID这种方案,一定只适用于并发量小,并且在可预见的未来并发量也不会增高的场景中,可能分表只是为了一些业务上的其他原因。

2、UUID

UUID的设计就是为了保证在分布式环境中能够生成全局唯一的识别码。

UUID的组成部分

(1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
(2)时钟序列。
(3)全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。

优点:

(1)UUID生成时非常方便,不需要借助任何额外的资源,直接本地调用相关API即可生成。
(2)不存在网络消耗。
(3)不存在并发以及可用性问题。

缺点:

(1)太长了,UUID是一个128bit的数值,生成时一般以36位长度的字符串表示,如:ecb404cc-7787-bb9b-bcdf-b2d73bf72542。
(2)无序,没有顺序就意味着增删改操作效率会变低。
(3)不具备业务含义,单从随机串中不能反映出任何业务上的信息。

3、Redis生成

Redis自身就提供了incr和incrby这样的自增命令,所以能保证生成的ID肯定是唯一且有序的。

优点:

性能高效,使用简单,ID有序

缺点:

(1)尽管现在的项目中Redis几乎是必备的,但如果项目中真的没有Redis,则就必须要引入,增加了一定的复杂度
(2)为了保证可靠性,则必须要开启Redis持久化,同时对于单点故障还需搭建主从、甚至集群,代价太高。

4、雪花算法

这是目前比较流行,采用较多的一种方式,其核心思想是,采用bigint(64bit)作为id生成类型,并将所占的64bit划分成多段。

其结构如下:

在这里插入图片描述

  • 第一位作为标识位,因为Java中long类型的时代符号的,因为ID位正数,所以第一位位0。
  • 接着41位时间戳,注意这里的时间戳并不是指当前时间的时间戳,而是值之间差(当前时间-开始时间),这里的开始时间一般是指ID生成器的开始时间,是由我们程序自己指定的。
  • 10 位的数据机器位,包含5位数据中心标识ID(datacenterId)和5位的机器标识ID(workerId),可以最多标识1024个节点(1<<10=1024)
  • 12 位序列,12位的计数顺序支持每个节点每毫秒((同一机器,同一时间戳)产生4096个ID序号(1<<12=4096)

优点:

(1)整体上生成的ID基本有序递增,对于数据库的索引性能更加友好
(2)本地生成,不依赖任何组件。
(3)没有网络消耗,直接内存生成,效率高,且可用性高。

缺点:

因为雪花算法的计算依赖于时间,在分布式环境中,如果发生系统时间回拨,则很有可能会产生重复ID的情况。

如何解决时间回拨的问题?

(1)关闭时钟同步
(2)抛异常,由上层业务解决
(3)如果回拨时间较短,可以等回拨时长过去后再生成
(4)如果回拨时间较长,也可以以匀出少量位(1~2 位)作为回拨位,一旦时钟回拨,将回拨位加1,也可得到不一样的ID。

美团Leaf-snowflake对于时钟回拨的解决方案
在这里插入图片描述

5、美团Leaf算法

Leaf这个名字是来自德国哲学家、数学家莱布尼茨的一句话: >There are no two identical leaves in the world > “世界上没有两片相同的树叶”

美团中有两种分布式ID方案,分别为:Leaf-segment和Leaf-snowflake方案

Leaf-segment

对于Leaf-segment,其核心思想是预先从数据库申请一批ID的方式,然后在内存中分配,当快分配完了以后,再向数据库请求一批ID,通过这样的方式就大大减少了访问数据库的次数。

这种方案存在安全性的问题,比如用它来生成订单号,则很容易就被别人计算出一天的订单量,所以Leaf-segment不适用于这类场景。

Leaf-snowflake

而Leaf-snowflake,其核心思想同原snowflake一样,只是在获取workerID时,为了解决服务规模较大,动手配置成本太高的问题,而使用Zookeeper持久顺序节点的特性自动对snowflake节点配置wokerID。

官网介绍

以下截图来自美团官方对于Leaf-segment方案的详细介绍

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/CSDN_WYL2016/article/details/114622675