您的位置:首页 > 其它

分布式 ID 生成策略

2015-10-26 00:00 288 查看
摘要: 浏览 OSCHINA 高手问答 记录 一下自己不知道的 内容 网上查到的资料

MongoDB objectid
ObjectId 是"_id" 的默认类型。它设计成轻量型的,不同的机器都能用全局唯一的同种方法方便地生成它。这是MongoDB 采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个服务器上同步自动增加主键值既费力还费时。MongoDB 从一开始就设计用来作为分布式数据库,处理多个节点是一个核心要求。后面会看到ObjectId 类型在分片环境中要容易生成得多。

ObjectId 使用12 字节的存储空间,每个字节两位十六进制数字,是一个24 位的字符串。由于看起来很长,不少人会觉得难以处理。但关键是要知道这个长长的ObjectId 是实际存储数据的两倍长。

如果快速连续创建多个ObjectId,会发现每次只有最后几位数字有变化。另外,中间的几位数字也会变化(要是在创建的过程中停顿几秒钟)。这是ObjectId 的创建方式导致的。12 字节按照如下方式生成:
前4 个字节是从标准纪元开始的时间戳,单位为秒。这会带来一些有用的属性。时间戳,与随后的. 5 个字节组合起来,提供了秒级别的唯一性。
由于时间戳在前,这意味着ObjectId 大致会按照插入的顺序排列。这对于某些方面很有用,如将其作为索引提高效率,但是这个是没有保证的,仅仅是“大致”。
这4 个字节也隐含了文档创建的时间。绝大多数驱动都会公开一个方法从ObjectId 获取这个信息。
因为使用的是当前时间,很多用户担心要对服务器进行时间同步。其实没有这个必要,因为时间戳的实际值并不重要,只要其总是不停增加就好了(每秒一次)。
接下来的3 字节是所在主机的唯一标识符。通常是机器主机名的散列值。这样就可以确保不同主机生成不同的ObjectId,不产生冲突。
为了确保在同一台机器上并发的多个进程产生的ObjectId 是唯一的,接下来的两字节来自产生ObjectId 的进程标识符(PID)。
前9 字节保证了同一秒钟不同机器不同进程产生的ObjectId 是唯一的。后3 字节就是一个自动增加的计数器,确保相同进程同一秒产生的ObjectId 也是不一样的。同一秒钟最多允许每个进程拥有2563(16 777 216)个不同的ObjectId

@宇智波唐嫣 的实现方案 学习一下
目前我正在写的 ID 生成服务的基本思路是这样的:
优点是逻辑简单,生成的 ID 短小,单个进程维护起来方便。缺点是知道生成的 ID 的上限(可以用很大的块部分解决)。
ID 生成服务管理多个不同名字的 ID 池 (pool)。

每种不同类型的 ID 属于不同的 ID 池,比如知乎会有用户 ID 池、回答 ID 池等等。

每个 ID 池由多个定长 ID 块 (block) 构成。

每个 ID 块包含一段连续的 ID,比如第一块是从 0~1023,第二块从 1024~2047,以此类推。

每个 ID 块并不完全分配,而是按照一个给定的填充率 (fill ratio) 随机选择来分配,比如假设填充率是 50%,那么每个 ID 块中只有大约一半的 ID 会被分配。也就是说每个 ID 块是有随机空洞的。

如果某个 ID 块中可用 ID 被分配完毕,服务会自动生成下一个新的 ID 块,并按照填充率去掉不可用 ID。生成新的 ID 块时需要记录下最后一个 ID 块的起始 ID。

已经分配过的 ID 会写入一个 append-only 日志。新的 ID 块生成时会创建一个新的 append-only 日志(因为旧的已经不需要了)。

服务重新启动的时候先拿到最后一个 ID 块的起始 ID,再读取 append-only 日志将已经分配过的 ID 剔除,再剔除掉部分未分配的 ID 以维持填充率。

客户端发送请求访问 ID 生成服务。服务端返回 ID 做为回复。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: