(9)分布式ID生成器解决方案(有问题)

一, 问题描述

分布式系统, 怎么生成全局unique ID? 单机自增 ID 就可以

(1)全局唯一、Shards 之间迁移不会受到 ID 生成方式的限制

(2)带上时间, 例 ID 的前 k 位是 Timestamp, 按时间排序

(3)不大于 64 bits,速度有要求. 例如, 在一个高吞吐量的场景中, 需要每秒生成几万个 ID (Twitter 最新的峰值到达了143,199 Tweets/s, 也就是 10万+/秒)

(4)整个服务最好没有单点

so,就不可以:UUID.randomUUID() ,128 bits, 没有Timestamp

中心服务器统一生成 :解决单点问题; 支持高吞吐率(例,从中心服务器批量获取一批 IDs, 提升 ID 产生吞吐率)

Flickr 的做法 (http://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/). 但他这个方案 ID 中没有带 Timestamp, 生成的 ID 不能按时间排序

二, 解决办法 :Twitter Snowflake

https://github.com/twitter/snowflake Snowflake 生成的 unique ID 的组成 (由高位到低位、63 bits最高位 0)):

Timestamp (毫秒级,41 bits) + 节点 ID (datacenter ID 5 bits + worker ID 5 bits) + sequence number(12 bits)

(9)分布式ID生成器解决方案(有问题)_第1张图片

unique ID 生成过程:

10 bits 的机器号, 在 ID 分配 Worker 启动的时候, 从一个 Zookeeper 集群获取 (保证所有的 Worker 不会有重复的机器号)

Timestamp: 生成新 ID时, 获取当前 Timestamp, 

sequence number 分两种情况:

(1)当前的 Timestamp 和前一个在同一毫秒中, 就用前一个 ID 的 sequence number + 1 作为新的 sequence number (12 bits); 如果本毫秒内的所有 ID 用完, 等到下一毫秒继续 (这个等待过程中, 不能分配出新的 ID)

(2)当前的 Timestamp 比前一个 ID 的 Timestamp 大, 随机生成一个初始 sequence number (12 bits) 作为本毫秒内的第一个 sequence number 整个过程中, 只是在 Worker 启动的时候会对外部有依赖 (需要从 Zookeeper 获取 Worker 号), 之后就可以独立工作了, 做到了去中心化.

是两个都要依赖吗?

异常情况讨论:

如果获取到时间戳比前一个已生成 ID 的 Timestamp 小怎么办? Snowflake 继续获取当前机器时间, 直到获取到更大的 Timestamp 才能继续工作 (等待过程, 不能分配出新的 ID)

如果 Snowflake 所运行机器时钟有大偏差时, 整个 Snowflake 系统不能正常工作 (偏差得越多, 分配新 ID 时等待的时间越久)

Snowflake 官方文档 (https://github.com/twitter/snowflake/#system-clock-dependency) 明确要求 "You should use NTP to keep your system clock accurate". 且最好把 NTP 配置成不会向后调整的模式., NTP 纠正时间时, 不会向后回拨机器时钟.

三, Snowflake 的其他变种

1. Boundary flake

http://boundary.com/blog/2012/01/12/flake-a-decentralized-k-ordered-unique-id-generator-in-erlang/

变化:

ID 长度扩展到 128 bits: 最高 64 bits 时间戳; 然后是 48 bits 的 Worker 号 (和 Mac 地址一样长); 最后是 16 bits 的 Seq Number 由于它用 48 bits 作为 Worker ID, 和 Mac 地址的长度一样, 

启动时不需 Zookeeper 的Worker ID. 完全去中心化 基于 Erlang 它这样做的目的是用更多的 bits 实现更小的冲突概率, 这样就支持更多的 Worker 同时工作. 同时, 每毫秒能分配出更多的 ID

2. Simpleflake

http://engineering.custommade.com/simpleflake-distributed-id-generation-for-the-lazy/

取消 Worker 号,

保留 41 bits 的 Timestamp(和 Snowflake 一致), 

sequence number 扩展到 22 bits; 完全靠随机产生 (可能出现重复) 

限制就每秒生成ID 不能太多 (最好小于 100次/秒, 如果大于 100次/秒的场景, Simpleflake 就不适用了, 切换回 Snowflake).

3. instagram 的做法

Table 分多个逻辑分片 (logic Shard), 数量可很大, 如 2000 

被存储到哪个数据库实例上面; 数据库实例不需要很多. 例如, 对有 2 个 PostgreSQL 实例的系统 (instagram 使用 PostgreSQL); 可以使用奇数逻辑分片存放到第一个数据库实例, 偶数逻辑分片存放到第二个数据库实例的规则

每个 Table 指定一个字段作为分片字段 (例如, 对用户表, 可以指定 uid 作为分片字段)

插入一个新的数据时, 先根据分片字段的值, 决定数据被分配到哪个逻辑分片 (logic Shard)

然后再根据 logic Shard 和 PostgreSQL 实例的对应关系, 确定这条数据应该被存放到哪台 PostgreSQL 实例上

unique ID 的组成:

41 bits: Timestamp (毫秒)

13 bits: 每个 logic Shard 代号 (最大支持 8 x 1024 个 logic Shards)

10 bits: sequence number; 每个 Shard 每毫秒最多可以生成 1024 个 ID

logic Shard 代号:

插入新记录, 根据 uid 来判断这条记录应该被插入到哪个 logic Shard 中.

假设当前要插入的记录会被插入到第 1341 号 logic Shard 中 (假设当前的这个 Table 一共有 2000 个 logic Shard)

新生成 ID 的 13 bits 段要填的就是 1341 这个数字

sequence number 利用 PostgreSQL 每个 Table 上的 auto-increment sequence 来生成:

如果当前表上已经有 5000 条记录, 那么这个表的下一个 auto-increment sequence 就是 5001 (直接调用 PL/PGSQL 提供的方法可以获取到)

然后把 这个 5001 对 1024 取模就得到了 10 bits 的 sequence number

优势: logic Shard 替换  Worker 号,知道被存放在哪,按 logic Shard 为单位迁移

推荐一篇文章:微信的海量IM聊天消息序列号生成

https://www.cnblogs.com/imstudy/p/9766549.html

https://zhuanlan.zhihu.com/p/59289093

问题:https://www.cnblogs.com/keeya/p/9347617.html 怎么实现全局递增的唯一ID?改进办法没看明白

你可能感兴趣的:((9)分布式ID生成器解决方案(有问题))