在后端系统中,每条记录都需要一个唯一的ID来进行标识。
虽然一开始听起来可能很琐碎,但在高度分布式的环境中生成全局唯一标识符实际上是一个具有挑战性的任务。
在本文中,让我们来看一下一些常见的已知ID生成算法。
Ticket 服务 - 集中式数据库
使用自增功能生成ID
Ticket 服务解决方案利用 SQL 数据库中的自增功能来生成唯一的ID。
使用集中式数据库服务器,Web 服务器插入一个新记录到数据库中以生成一个自增的ID。
CREATE TABLE `ID` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`stub` char(1) NOT NULL default '',
PRIMARY KEY (`id`),
UNIQUE KEY (stub)
);
REPLACE INTO ID (stub) VALUES ('a');
SELECT LAST_INSERT_ID();
与使用 INSERT INTO 命令不同,我们可以使用 REPLACE INTO 命令来减少数据库中的记录数量。
REPLACE INTO 命令以原子方式原地更新单行,并获取自增的主 ID,而无需创建新记录。
优点:
- 实现简单
- 生成的ID是64位的
- ID是顺序且可排序的
缺点:
- 只能使用1个表。多个表或数据库将导致ID冲突
- 由于只使用了1个表,数据库成为了单点故障
- 如果每秒的写入数量巨大,将会有写入瓶颈
Ticket 服务 - 集群式数据库
使用轮询路由请求
与使用一个数据库不同,我们可以使用多个具有偏移量的数据库,以避免单点故障和写入瓶颈。
偏移量 用于防止ID冲突。每个数据库通过 k,k 是正在使用的数据库服务器数量,增加其ID。
如上所示,如果使用了三个数据库,每次生成ID时,自增的ID增加3。
优点:
- 相对容易实现
- 生成的ID是64位的
- 能够在没有单点故障的情况下处理高吞吐量
缺点:
- 由于使用了多个数据库,生成的ID不能保证是可排序的
- 难以水平扩展。添加新数据库很棘手,因为它会影响偏移量。
Twitter Snowflake
Snowflake方法在不依赖数据库的情况下生成 64位的ID。
64位的ID被分成5个主要部分
ID分为5个主要部分:
- 时间戳(41位)
- 数据中心ID(5位)
- 机器ID(5位)
- 序列号(12位)
- 符号位(1位)
时间戳。自纪元以来的毫秒数。41位大约会在70年内溢出,对于大多数项目的寿命来说是安全的。
数据中心ID。服务器所在的数据中心。如果两个服务器在相同的时间收到相同的请求,则可以防止ID冲突。
机器ID。机器的ID。如果两台服务器在相似的数据中心中的相同时间收到相同的请求,则可以防止冲突。
序列号。对于在同一服务器上生成的每个ID,序列号会递增1,并在每毫秒重置为0。这可以防止在同一服务器上的ID冲突。
优点:
- ID大致是有序的
- 能够在没有单点故障的情况下处理高吞吐量
- 能够在机器之间无需协调地生成ID
- 能够水平扩展。
缺点:
- ID不是完全有序的
- 未来的ID是可预测的。对于安全要求较高的应用程序可能不理想
- 需要一个Zookeeper来跟踪机器ID。
MongoDB ObjectID
MongoDB为每个新文档创建一个唯一的对象ID。
对象ID由 MongoDB驱动程序生成而不是数据库。这意味着可以在服务器上生成对象ID,而不依赖于MongoDB数据库。
MongoDB对象ID是一个96位的ID
与Snowflake方法类似,MongoDB对象ID分为4个部分。对象ID是一个96位的ID。
- 时间戳(32位)
- 机器ID(24位)
- 进程ID(16位)
- 计数器(24位)
大部分字段与Snowflake方法中提到的字段相似。
由于同一台机器上可能运行多个线程或进程,因此进程ID可以区分在不同进程中由同一台机器生成的对象ID。
优点:
- 能够在没有单点故障的情况下处理高吞吐量
- 能够在机器之间无需协调地生成ID
- 能够水平扩展。
缺点:
- 依赖第三方数据库解决方案
- ID的长度为96位,而不是64位,需要更多的存储空间。
UUID(通用唯一标识符)
128位UUID的示例
通用唯一标识符是一个128位的数字,包括多个部分,例如时间、节点的MAC地址或MD5哈希的命名空间。
有一组标准化的算法用于生成UUID,多年来已经发布了5个不同版本的UUID,以适应不同的需求。
这些算法相当冗长,因此我们不会详细介绍它们。我们将更多地关注它的优缺点。
优点:
- 它是一个128位的ID,保证是唯一的
- 可以独立生成,无需依赖任何第三方服务
- 它是随机的和安全的。下一个ID是不可预测的。
缺点:
- 它很大,在MySQL中索引不佳
- 它不是有序的
结论
在分布式环境中实现高度可扩展和可用的ID生成器并不是微不足道的。