分布式系统中唯一ID的生成方法

在一个MySQL集群中,想要生成一个数据库的全局Unique ID,要满足以下条件:

  1. 保证生成的ID唯一;
  2. 以后数据在多个node节点之间迁移时,不会受到ID生成方法的限制;
  3. 生成的ID信息最好不超过64bit;
  4. 生成的ID信息最好带上时间信息,如ID的前k位是Timestamp,这样能够直接通过前k位的排序来针对数据用时间排序;
  5. 生成ID的速度要快,如一个高吞吐量的场景中,需要每秒生成几万个ID;
  6. 整个服务最好没有单点问题。
如果没有上述问题,我们可以直接使用UUID.randomUUID()接口来生成unique ID.

下面我们给出两个生成方法:一个是Redis法,一个是Twitter的Snowflake

当使用数据库生成ID性能不能达到要求时,可以使用Redis生成ID。这主要依赖于Redis是单线程的,所以生成的ID也是全局性唯一的,使用Redis的原子操作incr和incrby实现。例如一个集群中有5台redis,可以初始化每台redis的值分别为1,2,3,4,5,然后步长都为5,各个redis生成的id为:

A:1,6,11,16,21

B:2,7,12,17,22

C:3,8,13,18,23

D:4,9,14,19,24

E:5,10,15,20,25

负载在哪台服务器要事先定好,后期修改比较麻烦,步长和初始值也需要事先知道,单点问题也能解决,比较适合redis方式的是生成从0开始的流水号。如订单号=日期+当日自增号,每天在redis中生成一个key,使用incr进行累加。

优点:不依赖于数据库,灵活方便,性能优于数据库;

缺点:系统没有redis时,需要特地引入redis集群来生成id,代价较大,复杂度也大,需要的编码工作也大。


snowflake是twitter的分布式id生成算法,结果是一个long型的ID,其思想为:

unique ID 的组成 (由高位到低位):

  • 41 bits: Timestamp (毫秒级)
  • 10 bits: 节点 ID (数据中心 ID 5 bits + 机器 ID 5 bits)
  • 12 bits: 毫秒内的流水号(意味着每个节点在每毫秒可以生成4096个ID)
一共 63 bits (最高位是 0)

下面说一下生成过程:
1)10 bits 的机器号, 在 ID 分配 Worker 启动的时候, 从一个 Zookeeper 集群获取 (保证所有的 Worker 不会有重复的机器号)
2)41 bits 的 Timestamp: 每次要生成一个新 ID 的时候, 都会获取一下当前的 Timestamp, 然后分两种情况生成流水号:
3)如果当前的 Timestamp 和前一个已生成 ID 的 Timestamp 相同 (在同一毫秒中), 就用前一个 ID 的 流水号+ 1 作为新的 流水号 (12 bits); 如果本毫秒内的所有 ID 用完, 等到下一毫秒继续 (这个等待过程中, 不能分配出新的 ID)
4)如果当前的 Timestamp 比前一个 ID 的 Timestamp 大, 随机生成一个初始 流水号 (12 bits) 作为本毫秒内的第一个 流水号
整个过程中,只有在work启动时会对外部有依赖(需要zookeeper获取worker号),之后就可以独立工作。

优点:
1)不依赖于数据库,灵活方便,且性能优于数据库。
2)ID按照时间在单机上是递增的。

缺点:
1)在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会出现不是全局递增的情况。



你可能感兴趣的:(数据库)