分布式基础(8)-常见分布式ID生成方案

本文主要参考自相关书籍和网络文章,并附上自身的一些理解,如有遗漏或错误,还望海涵并指出。谢谢!

一.什么是分布式ID

分布式ID即指的是在分布式系统中,全局的唯一标识;分布式ID其实与单机下的ID区别不大,其作用都是起到标识某个订单或数据、消息等,使其在分布式系统中全局唯一。

首先来思考一下,为什么会出现分布式ID呢?单机形式的ID生成方案为什么不能解决分布式ID的问题呢?

假设我们的订单生成服务由两台服务器同时来提供,并且每台机器上生成ID的规则如下:获取当前时间戳和特定序号自增值组合在一起。
在这里插入图片描述

由于请求是并发的,那么服务器1和2生成的时间戳一样,假设为151515(略去许多位),并且请求同时到达数据库,获取到的递增值为23,那么两个服务器都会生成同一个ID,造成冲突。

当然,这个场景是模拟的,只是为了还原一种可能出现分布式ID冲突的情况,可以看出单机的ID生成方案并不用适用于分布式情景下的ID生成,所以出现了许多的方案来解决这个问题。

二.常见分布式ID生成方案

1.UUID

UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。

按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。由以下几部分的组合:当前日期和时间、时钟序列、全局唯一的IEEE机器识别号(如果有网卡,从网卡获得,没有网卡以其他方式获得)。

UUID的的缺陷在于生成的结果串会比较长并且为字符串形式。

在Java中,可以使用以下方式生成UUID,由于每台服务器的MAC地址和网卡地址肯定是不一样的,所以就算是同一时间生成的ID也会有所不同:

String uuid = UUID.randomUUID().toString();

uuid:f2aca1c1-858b-4cdf-a5b9-999a92414504

2.数据库ID自增

一般主流数据库如Oracle、MySQL都自带表数据主键自增功能,所以我们可以借助数据库来为我们实现全局唯一的ID。

由于分布式数据库的起始自增值一样所以才会有冲突的情况发生,那么我们将分布式系统中数据库的同一个业务表的自增ID设计成不一样的起始值,然后设置固定的步长,步长的值即为分库的数量或分表的数量。

在MySQL中,可以为每台MySQL独自设置自身的初始ID以及递增步长来实现分布式ID的生成:

  • auto_increment_offset:表示自增长字段从那个数开始,他的取值范围是1 … 65535。
  • auto_increment_increment:表示自增长字段每次递增的量,其默认值是1,取值范围是1 … 65535。

假设有三台机器,则DB1中order表的起始ID值为1,DB2中order表的起始值为2,DB3中order表的起始值为3,它们自增的步长都为3,则它们的ID生成范围如下图所示,可以看出每个数据库生成的ID都不同。

在这里插入图片描述

使用数据库生成ID的方式可以保证ID的唯一性,并且实现起来也很简单,但是由于数据库只能做到数字自增,但是普通的数字是不适合做一些业务的ID的,譬如订单号,不能用23、24、25这种形式来标识,因为规律性太强并且从其中不能够解析出其他更有用的数据了(譬如时间戳之类)。

但是数据库自增ID很适合做主键物理ID,而业务逻辑ID应该选择其他方式来生成。

3.Redis

现在来说说Redis生成唯一ID的方案。

我们可以借助Redis的incr或incrby命令的原子特性来获取全局递增的序列号,来构建分布式ID:

GID = 时间戳 + 机器号 + 全局自增的Redis序列号

在这里插入图片描述

这种做法需要额外借助Redis服务来实现,并且需要保证Redis的可用性,如果Redis不可用的话(网络问题、宕机等)那么也会导致ID生成失败。

4.Snowflake-雪花算法

说到分布式ID生成方案就不得不提大名鼎鼎的Snowflake-雪花算法。

雪花算法是推特为了解决分布式ID问题而研发出的一种方法,主要的思路与UUID的思路一致,不同的是雪花算法会算得一个64位的Long型ID,而不是一条字符串。

在这里插入图片描述

  1. 第1bit不使用,默认为0,因为在许多语言中Long的最高位都只用以表示为符号为,这里默认生成的ID为正值。除开高位的1bit外的63bit全部都需要使用。
  2. 前41bit表示毫秒级的时间戳,这个时间戳指的是差值时间戳,而不是时刻时间戳。41bit能表达的数字范围为0 ~ 2^ 41个,所以能够表达2^ 41毫秒也就是69年。所以一个由雪花算法生成的ID,最长能够保证69年内不出现重复的,因为在2^ 41毫秒之后,时间戳会重新将差值置为0,开始出现重复。
  3. 中间10bit用以表示机器ID,最多可以容纳2^ 10也就是1024台机器组成。还可以将着10bit划分为:5bit表示数据中心,5bit表示机器ID。
  4. 后12bit用以表示自增序列号,12bit最多可表示2^ 12 = 4096个数。

所以在极限情况下,假设我们有1024台机器,在1毫秒之内我们可以生成的全局ID有:

1024 * 4096 = 400万

在1s中之内可以生成的ID有:

1024 * 4096 * 1000 = 40亿

所以在不考虑机器性能的情况下,通过雪花算法,1毫秒可以获取到400万个不重复的ID,1秒可以获取40亿个ID,这是一个十分可观的数字。但是实测来说,单机的机器1秒钟大概可以生成30万个ID。

具体的代码实现以及性能测试可以参考这两篇文章:

  • http://www.machengyu.net/tech/2019/12/04/snowflake.html
  • https://yq.aliyun.com/articles/713964
5.其他:Leaf和UidGenerator

Leaf和UidGenerator分别由美团与百度研发,其原理与雪花算法类似,不过提供了更多的可选项和自定义操作,这里就不再详细说明了。

三.总结

  1. UUID:实现简单,不需要网络,可以在本地生成全局唯一ID。但是生成的ID为字符串类型,并且长度较长。
  2. 数据库ID自增:实现简单,需要与数据库连接,但是生成的ID只能为简单的数字,不适合做业务ID。
  3. Redis:需要外加Redis服务,成本较高,需要设置好Redis以提供高可用服务。但是容易定制和拓展。
  4. 雪花算法:需要网络(要借助zk来注册自增序号),效率高,可生成Long型ID,理论上一个ID可有69年的过期时间以及1毫秒内最多可并行生成400万个ID。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoringError

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值