5.1 KiB
6.7. 分布式 id 生成器
有时我们需要能够生成类似 MySQL 自增 ID 这样不断增大,同时又不会重复的 id。以支持业务中的高并发场景。比较典型的,电商促销时,短时间内会有大量的订单涌入到系统,比如每秒 10w+。明星出轨时,会有大量热情的粉丝发微薄以表心意,同样产生短时间大量的消息。
在插入数据库之前,我们需要给这些消息/订单先打上一个 ID,然后再插入到我们的数据库。对这个 id 的要求是希望其中能带有一些时间信息,这样即使我们后端的系统对消息进行了分库分表,也能够以时间顺序对这些消息进行排序。
Twitter 的 snowflake 算法是这种场景下的一个典型解法。先来看看 snowflake 是怎么一回事:
datacenter_id sequence_id
unused
│ │
│ │ │
│ │ │
│ │ │ │ │
│ │ │ │ │
▼ │◀────────────────── 41 bits ────────────────────▶│ ▼ ▼
┌─────┼──────────────────────────────────────────────────────┼────────┬────────┬────────────────┐
│ 0 │ 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0 │ 00000 │ 00000 │ 0000 0000 0000 │
└─────┴──────────────────────────────────────────────────────┴────────┴────────┴────────────────┘
▲ ▲
│ │
│ │
│ │
│ │
│ │
│ │
time in milliseconds worker_id
首先确定我们的数值是 64 位,int64 类型,被划分为四部分,不含开头的第一个 bit,因为这个 bit 是符号位。用 41 位来表示收到请求时的时间戳,单位为毫秒,然后五位来表示数据中心的 id,然后再五位来表示机器的实例 id,最后是 12 位的循环自增 id(到达 1111 1111 1111 后会归 0)。
这样的机制可以支持我们在同一台机器上,同一毫秒内产生 2 ^ 12 = 4096 条消息。一秒共 409.6w 条消息。从值域上来讲完全够用了。
数据中心 + 实例 id 共有 10 位,可以支持我们每数据中心部署 32 台机器,所有数据中心共 1024 台实例。
表示 timestamp 的 41 位,可以支持我们将时间推进到 2039/9/7 23:47:35
,暂时也够用了。如果不够用,我们就从数据中心 id 中接一位出来,可以用到遥不可及的未来了。
worker id 分配
timestamp,datacenter_id,worker_id 和 sequence_id 这四个字段中,timestamp 和 sequence_id 是由程序在运行期生成的。但 datacenter_id 和 worker_id 需要我们在部署阶段就能够获取得到,并且一旦程序启动之后,就是不可更改的了(想想,如果可以随意更改,可能被不慎修改,造成最终生成的 id 有冲突)。
一般不同数据中心的机器,会提供对应的获取数据中心 id 的 api,所以 datacenter_id 我们是可以在部署阶段简单地获取到的。而 worker_id 是我们逻辑上给机器分配的一个 id,这个要怎么办呢?比较简单的想法是由能够提供这种自增 id 功能的工具来支持,比如 MySQL:
开源实例
gosnowflake
sonyflake