流行方案

现在流行的分布式ID方案很早就已经稳定了 基本的分布式 ID 都有一下特性

  • 全局唯一
  • 是否递增
  • 能否摆脱单机限制

实现方案

  • 完全依赖数据源方式: —- ID的生成规则,读取控制完全由数据源控制,常见的如数据库的自增长ID,序列号等,或Redis的INCR/INCRBY原子操作产生顺序号等。
  • 半依赖数据源方式: —- IID的生成规则,有部分生成因子需要由数据源(或配置信息)控制,如snowflake算法。
  • 不依赖数据源方式: —- ID的生成规则完全由机器信息独立计算,不依赖任何配置信息和数据记录,如常见的UUID,GUID等。

方案实践

实践方案适用于以上提及的三种实现方式,可作为这三种实现方式的一种补充,旨在提升系统吞吐量,但原有实现方式的局限性依然存在。

MongoDB ObjectId()

MongoDB 在之前

MongoDB 3.2 及其之前

  • 4-byte 时间戳
  • 3-byte 主机ID
  • 2-byte 进程ID
  • 3-byte 递增序列

MongoDB 3.2 之后

  • 4-byte 时间戳
  • 5-byte 随机数
  • 3-byte 递增序列

MySQL 自增ID

优点

  • 使用简单,维护成本低
  • 天然单调递增,某些场景使用很方便

缺点

  • 本身不支持分片之类都操作,无法摆脱单机TPS都局限
  • 主从同步切换时很容易出现问题

Redis 自增ID

如果数据库性能不能支撑业务时可以考虑使用 Redis 来 生成唯一ID。

例如 订单号 = 日期 + 当日增长序列 - INCR , 或者每次用 INCRBY 批量获取部分ID。在应用内部分发,等消耗完来再次获取 -

优点

  • 不依赖业务数据库,性能比普通数据库好
  • ID有序

缺点

  • 需要做一定等编码和配置,工作量较大

UUID

UUID是通用唯一识别码(Universally Unique Identifier)的缩写,开放软件基金会(OSF)规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素。利用这些元素来生成UUID。

  • uuid1() 序号 + 时间 + 主机ID
  • uuid3() 根据传入的参数 HASH
  • uuid4() 全部随机
  • uuid5() 根据传入的参数 SHA-1

优点

  • 性能非常高:本地生成,没有网络消耗。

优点

  • 性能非常高:本地生成,没有网络消耗。
  • 以主机信息 HASH 或者 SHA 加密要考虑种子数据碰撞的可能性,
  • MAC 地址加密是可以被跟踪的,历史上有根据uuid -> mac 地址 找人的案例
  • uuid4 这种全部随机的方式是非常容易被猜中的,
  • 现在很多应用的都部署在容器中,它们都主机名和网卡高度相似,这会提高碰撞都几率

Snowflake

snowflak和python的uuid类似,都是时间戳 + 主机信息 + 时钟序列

avatar

  • 1位,不用。二进制中最高位为1的都是负数,但是我们生成的id一般都使用整数,所以这个最高位固定是0
  • 41位,用来记录时间戳(毫秒)。
    • 41位可以表示2^41-12
    • 如果只用来表示正整数(计算机中正数包含0),可以表示的数值范围是:0 至 2^41-12^41−1,减1是因为可表示的数值范围是从0开始算的,而不是1。
    • 也就是说41位可以表示2^41-12^41−1个毫秒的值,转化成单位年则是(2^41-1) / (1000 * 60 * 60 * 24 * 365) = 69(2^41−1)/(1000∗60∗60∗24∗365)=69年
  • 用来记录工作机器id
    • 可以部署在2^10=1024个节点,包括5位datacenterId和5位workerId
    • 5位(bit)可以表示的最大正整数是 2^5-1=31 即可以用0、1、2、3、….31这32个数字,来表示不同的datecenterId或workerId
  • 12位,序列号,用来记录同毫秒内产生的不同id。
    • 12位(bit)可以表示的最大正整数是 2^{12}-1 = 4095−1=4095,即可以用0、1、2、3、….4094这4095个数字,来表示同一机器同一时间截(毫秒)内产生的4095个ID序号

优点

  • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的
  • 时间戳在高位,序列在地位,整个ID是递增的
  • 中间服务器信息部分可以灵活定制

缺点

  • 强制依赖机器时间,如果发生时钟回拨,整个uuid可能会出现重复

大厂方案

  • 在服务器信息方面做来可能配置化
  • 时钟偏移都问题得到了解决
  • 与本身业务使用都方式结合严密
  • 高可用
  • 在递增、性能、等方面都做了取舍

总结

  • 市面上大多数的方案都大同小异。
  • 任何一件看起来很简单的事情,在海量的访问量下都会变得不简单。
  • 尽量不要过度设计,例如订单系统一天只有100W的订单,平均下来一秒的订单在10条左右,其实 UUID 就能满足。
  • 很多方案依赖主机等静态信息,使用序注意,不然会回退单机且可能出现重复ID。

参考文献