Distributed ID Generator
Table of content:
About
这篇文章是对业内 ID 生成方案的一些调研和思考, 后续会增加一些实现。
发号器需要哪些特性
特性 | 说明 |
---|---|
唯一性 | 这是发号器的基础要求 |
高并发 | 能够在很多时间内生成大量的唯一 id |
高可用 | 不能一台机器 down 了,整个发号器都不可用 |
按照时间排序 | 构建索引,按照范围查询 |
可反解 | 通过反解,能够快速定位问题 |
格式优雅 | 通常对外的 id 要求足够短 |
不容易被猜出 | 比如自增 id 就是顺着 + 1,这样很容易被猜到 |
方案
UUID
UUID 算法,生成的 ObjectId 占 12 个字节,由以下几个部分组成,
- 4 个字节表示的 Unix timestamp,
- 3 个字节表示的机器的 ID
- 2 个字节表示的进程 ID
- 3 个字节表示的计数器
UUID 是一类算法的统称,具体有不同的实现。UUID 的有点是每台机器可以独立产生 ID,理论上保证不会重复,所以天然是分布式的,缺点是生成的 ID 太长,不仅占用内存,而且索引查询效率低。UUID 是由 128 位二进制组成,一般转换成十六进制,然后用 String 表示。
生成策略包括:
- 随机生成
- 通过当前时间,随机数,和本地 Mac 地址来计算出来
- 基于名字的 UUID,通过计算名字和名字空间的 MD5 来计算 UUID
优点:本地生成,生成简单,性能好,没有高可用风险
缺点:长度过长,存储冗余,且无序不可读,查询效率低
数据库自增 ID
使用数据库的 id 自增策略,如 MySQL 的 auto_increment。并且可以使用两台数据库分别设置不同步长,生成不重复 ID 的策略来实现高可用, 一个例子:
Server1:
auto-increment-increment = 2
auto-increment-offset = 1
Server2:
auto-increment-increment = 2
auto-increment-offset = 2
优点:数据库生成的 ID 绝对有序,高可用实现方式简单
缺点:
- 需要独立部署数据库实例,成本高,有性能瓶颈
- 分库分表会带来问题,需要进行改造
- 不是严格实现的全局递增
- 简单递增容易被其他人猜测利用
Flickr 使用了类似的方案, http://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/
Redis
Redis 是单线程的,所以也可以用生成全局唯一的 ID。可以用 Redis 的原子操作 INCR 和 INCRBY 来实现。
使用 Redis 集群来获取更高的吞吐量。假如一个集群中有 5 台 Redis。可以初始化每台 Redis 的值分别是 1,2,3,4,5,然后步长都是 5。各个 Redis 生成的 ID 为:
A:1,6,11,16,21
B:2,7,12,17,22
C:3,8,13,18,23
D:4,9,14,19,24
E:5,10,15,20,25
优点:
- 不依赖于数据库,灵活方便,且性能优于数据库。
- 数字 ID 天然排序,对分页或者需要排序的结果很有帮助。
缺点:
- 如果系统中没有 Redis,还需要引入新的组件
- 需要配置的工作量比较大
Twitter 的 snowflake 算法
Twitter 利用 zookeeper 实现了一个全局 ID 生成的服务 Snowflake:https://github.com/twitter-archive/snowflake
如上图的所示,Twitter 的 snowflake 算法下面几部分组成:
- 1 位一般是符号位,不做处理
- 41 位的时间序列,精确到毫秒,可以使用 69 年
- 10 位的机器标识,最多支持部署 1024 个节点
- 12 位的序列号,支持每个节点每毫秒产生 4096 个 ID 序号,最高位是符号位始终为 0。
这种方案性能好,在单机上是递增的, 但是不是全局递增,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会出现不是全局递增的情况。
而且这个项目在 2010 就停止维护了,但这个设计思路还是应用于其他各个 ID 生成器及变种。
国内有很多厂家基于 snowflake 算法进行了国产化,例如:
- 百度的 uid-generator:https://github.com/baidu/uid-generator
- 美团 Leaf:相关文章 GitHub
TDDL
TDDL 是阿里的分库分表中间件,它里面包含了全局数据库 ID 的生成方式,主要思路:
- 使用数据库同步 ID 信息。
- 每次批量取一定数量的可用 ID 在内存中,使用完后,再请求数据库重新获取下一批可用 ID,每次获取的可用 ID 数量由步长控制,实际业务中可根据使用速度进行配置。
- 每个业务可以给自己的序列起个唯一的名字,隔离各个业务系统的 ID。
常见问题
基于时间戳的方案的时间戳同步以及时钟回拨等问题怎么处理(todo)
Reference 和推荐阅读
- Leaf, 美团点评分布式 ID 生成系统 - 美团技术团队 https://tech.meituan.com/2017/04/21/mt-leaf.html
- https://juejin.im/post/5bb0217ef265da0ac2567b42
- 生成全局唯一 ID 的 3 个思路 https://mp.weixin.qq.com/s?__biz=MzI4MTY5NTk4Ng==&mid=2247489561&idx=1&sn=7396f373af4efa62ba4dbecc6d7f83b3&source=41#wechat_redirect
- https://www.jianshu.com/p/4e2febab63ad
- Instagram 的方案 https://engineering.instagram.com/sharding-ids-at-instagram-1cf5a71e5a5c
- Flickr 的方案 http://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/
关于头图
拍摄自水魔方