本文主要介绍在一个分布式系统中, 如何去生成全局唯一的 ID。
前言
单纯的生成全局ID并不是什么难题,生成全局的 unique ID 要满足以下需求:
- 保证生成的 ID 全局唯一
- 今后数据在多个 Shards 之间迁移不会受到 ID 生成方式的限制
- 生成的 ID 中最好能带上时间信息, 例如 ID 的前 k 位是 Timestamp, 这样能够直接通过对 ID 的前 k位的排序来对数据按时间排序
- 生成的 ID 最好不大于 64 bits
- 生成 ID 的速度有要求. 例如, 在一个高吞吐量的场景中, 需要每秒生成几万个 ID (Twitter 最新的峰值到达了 143,199
Tweets/s, 也就是 10万+/秒) - 整个服务最好没有单点
问题描述
当用户量激增 系统架构演进到一定的阶段,常常会设计到分库分表,
例如根据id对用户表(t_user)进行分表,[0,999999]保存在t_user_0表,[1000000,1999999]保存在t_user_1表中,依次类推,怎么给这些用户生成全局的 unique ID?
全局ID产生的几种方式
1、数据库自增id
当服务使用的数据库只有单库单表时,可以利用数据库的auto_increment来生成全局唯一递增ID.
优势:
- 简单,无需程序任何附加操作
- 保持定长的增量
- 在单表中能保持唯一性
劣势:
- 高并发下性能不佳,主键产生的性能上限是数据库服务器单机的上限。
- 水平扩展困难,在分布式数据库环境下,无法保证唯一性。
2、UUID
一般的编程语言中会自带UUID的实现,比如Java中UUID方式UUID.randomUUID().toString(),可以通过服务程序本地产生,ID的生成不依赖数据库的实现。
优势:
- 本地生成ID,不需要进行远程调用。
- 全局唯一不重复。
- 水平扩展能力非常好。
劣势:
- ID有128 bits,占用的空间较大,需要存成字符串类型,索引效率极低。
- 生成的ID中没有带Timestamp,无法保证趋势递增
3、Flickr 的全局主键生成方案
flickr巧妙地使用了MySQL的自增ID,及replace into语法,十分简洁地实现了分片ID生成功能。详见 :http://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/
比如创建64位的自增id: <