分布式系统的唯一ID

需求

为什么需要唯一ID

让分布式系统中的需要辨别的元素,都能有唯一的辨识标志。 几乎所有的业务系统,都有生成一个记录标识的需求,例如:

  1. 消息标识:message-id
  2. 订单标识:order-id
  3. 帖子标识:tiezi-id

为什么需要趋势有序

记录标识上的查询,往往又有分页或者排序的业务需求,例如:

  1. 拉取最新的一页消息:select message-id order by time limit 100
  2. 拉取最新的一页订单:select order-id order by time limit 100
  3. 拉取最新的一页帖子:select tiezi-id order by time limit 100

所以往往要有一个time字段,并且在time字段上建立普通索引(non-cluster index)。

普通索引存储的是实际记录的指针,其访问效率一般会比聚集索引慢,如果记录标识在生成时能够基本按照时间有序,则可以省去这个time字段的索引查询:select message-id (order by message-id) limit 100但是,能这么做的前提是,message-id的生成基本是趋势时间递增的

怎么实现唯一ID

UUID

UUID就是为了要在分布式环境中产生唯一标示符而发布的一个标准。标准中规定UUID长度为16Bytes(128Bits),一般将其表示为550e8400-e29b-41d4-a716-446655440000这种16进制格式,同时将其分为5部分,每部分用-分割,各部分长度分别为8,4,4,12。现在使用的UUID算法有5个版本,分别使用5种不同的算法计算产生。

  1. UUID1: 依据当前计算机的MAC地址时钟来生成uuid。
  2. UUID2: 和版本1类似,不过使用域标示符本地UID代替了版本1中的时钟信息。
  3. UUID3: 根据url,域标示符等标示符做MD5 Hash产生的。
  4. UUID4: 根据产生的随机数来生成。
  5. UUID5: 和版本3类似,只不过替换成了SHA-1算法。

优点:

  1. 本地生成,不需要控制中心管理,成本低
  2. 性能好

缺点:

  1. id共128Bits太长
  2. id间没有次序关系,不能隐含信息

mogodb ObjectId

MongoDB中每一条记录都有一个id字段用来唯一标示本记录。如果用户插入数据时没有显示提供id字段,那么系统会自动生成一个。ObjectID一共12Bytes,设计的时候充分考虑了分布式环境下使用的情况,所以能保证在一个分布式MongoDB集群中唯一。ObjectID格式如下:

0        4      7    9      12  
+--------+------+----+------+
|time    |pc    |pid |inc   |
+--------+------+----+------+

0~4 Byte是Unix Timestamp。 4~7 Byte是当前机器“hostname/mac地址/虚拟编号”其中之一的MD5结果的前3个字节。 7~9 Byte是当前进程的PID。 9~12Byte是累加计数器或是一个随机数(只有当不支持累加计数器时才用随机数)。 最后生成的仍然是一个用16进制表示的串,如47cc67093475061e3d95369d。这里MongoDB的ObjectID相对UUID有个很大的优点就是ObjectID是时间上有序的。另外还有ObjectID本身也包含了很多其它有用的信息,通过直接解码ObjectID即可直接获得这些信息。

优点:

  1. 时间有序
  2. 隐含信息,可在业务中结合加以利用。

缺点:

  1. 当time段一样,由于MD5只取前3Byte,有可能造成pc段一样,这样就有可能有重复的id。
  2. ID 间隙较大(当某一段时间不生成id,那么这个time段浪费很多空间)

snowflack

Snowflake是twitter开源的一款独立的适用于分布式环境的ID生成服务器。生成的ID是64Bits,同时满足高性能(>10K ids/s),低延迟(<2ms)和高可用。与MongoDB ObjectID类似这里生成的ID也是时间上有序的。编码方式也和ObjectID类似,如下:

0           41     51     64  
+-----------+------+------+
|time       |pc    |inc   |
+-----------+------+------+

前41bits是以微秒为单位的timestamp。 接着10bits是事先配置好的机器ID。 最后12bits是累加计数器。

有缺点与MongoDB ObjectId类似。但是只要机器ID不重复,应该不会出现重复的ID。

Instagram采用的方式

Instagram要将其中存储的图片分片到多个PostgreSQL中,其中生成ID的方案和MongoDB ObjectID类似。整个ID的长度为64Bits,设定为这个长度是为了优化在redis中的存储。ID的编码格式如下:

41bits以微秒为单位的timestamp,时间起点从2011-01-01开始。 13bits表示进行逻辑分片的Shard ID。 10bits表示一个累加计数器。 ID的生成逻辑用PL/PGSQL语言写到PostgreSQL数据库中,当每次插入数据时由数据库自动计算生成。 与上面优缺点类似。

Leaf

主要参考:http://wiki.sankuai.com/pages/viewpage.action?pageId=465861190。 利用step设置每个服务能从数据库拿到的号段大小,能充分的利用id的空间,能保证号段内各个id的时间顺序,但是不能保证号段间时间上的顺序。

主要优点是id占用字节少(64bits),能充分利用空间,几乎没有间隙(按作者说,除非服务器宕机,这种可能会比较小)。

我的想法:

假设应用生命周期为30年(一般极少有应用生命周期30年,linux系统到现在也不超过30年,就算30年到时候也该换方案和架构了),如果时间的精确度是微秒,30年需要通过12位整数保存,使用二进制保存所有12位整数需要大约40位二进制;如果是秒,需要9位整数保存,使用大约30位二进制。假设63位中(除最高位,最高位应该是符号位。)

  • 使用微秒方案:前40位给时间,那么还有23位可以给step区间(可表示8百万个整数,相当于容量为1微秒8百万个id)。
  • 使用秒方案:前30位给时间,那么还有33位可以给step区间(每秒产生id数量与使用微妙方案一秒产生的id数量相同)。
  • 使用X秒方案:以此类推

对比秒方案和微秒方案,(X)秒方案可能由于时间对系统能表述的id空间的浪费更少,而且整体能表述的id数量不变,但是递增趋势更弱(使用微妙,递增趋势更强)。

总结

一般在分布式系统中,与生成唯一ID有关的因素可以来自:

  • 时间(基于某一时刻到现在的相对时间,更节约空间)
  • 机器逻辑区分ID(如:机器ID,存储的分片)
  • 机器的硬件信息(如:MAC地址等)
  • 局部自增

转载于:https://my.oschina.net/hgfdoing/blog/702986

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值