分布式 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 做为回复。
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 做为回复。
相关文章推荐
- jsp
- mysql_multi 多实例
- Kettle Web端的使用
- hibernate session
- MindMapper中的主题该怎么实现收缩
- 一些代码片段
- NodeJS环境搭建
- Curve Mode功能解释
- 蜂窝教育Java培训 让大学生高薪就业
- 使用qsort()和bsearch()函数对值和字符串进行排序和查找
- Java记录 -45- List的toString方法
- codeforces #39E
- out.write()和out.print()区别
- 多款优秀的 JS MVC 框架对比
- 货币型基金接口 开放式基金数据调用
- 上海电影院分布数据接口
- FastReport.Net v2015.4.1发布【附下载】
- Mysql 优化
- 在Eclipse中导入SVN库里的Maven项目
- MYSQL中LIMIT用法