ID生成算法-雪花算法介绍及实现

2020-07-06 16:42:13 浏览数 (1)

1. SnowFlake 算法介绍

雪花算法是由 Twitter 公司开源的可在分布式系统中产生一个全局唯一 ID 的算法。最初 Twitter 把存储系统从 MySQL 迁移到 Cassandra,因为 Cassandra 没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务。

SnowFlake 算法生成的 ID 是一个 64 位的整数,它的结构如下图所示:

  • 第一部分:1bit 符号位,由于都是生成 ID 都是正数,所以第一位统一为0;
  • 第二部分:41 bit 时间戳,单位是毫秒,41 位可以表示的数字多达 2^41 - 1,换算成年就是 69 年;
  • 第三部分:5 bit 机房 ID,代表最多支持 32 个机房;
  • 第四部分:5 bit 机器 ID,代表每个机房最多支持 32 台机器;
  • 第五部分:12 bit,记录同一时间(毫秒)内产生的不同 id,也就是说同一毫秒内可以产生4096个不同 id。

SnowFlake 生成的 ID 整体上按照时间自增排序,并且整个分布式系统不会产生 ID 碰撞(由 DataCenterID 和 WorkerID 区分),并且效率较高。

2. 实现

在实现 SnowFlake 基本功能的基础上,增加部分拓展功能:

  • 定义开始时间戳,默认为 2020/01/01 08:00:00,如果使用默认的时间戳位数,那么该程序生成 ID 大概可以使用到 2089 年;
  • 机房 ID 、机器 ID 和 序列 ID 三个数据段长度可以自定义,通过构造函数传入。

2.1 方法介绍

  • timeGen
    • 描述:生成当前毫秒时间戳,相对于 2020年1月1日 8:00:00
    • 属性:private
    • 返回值:当前毫秒时间戳
  • tilNextMillis
    • 描述:阻塞直到下一毫秒
    • 属性:private
    • 返回值:下一毫秒时间戳
  • nextId
    • 描述:生成一个新的 ID
    • 返回值:新 ID

2.2流程图

2.3 代码实现

代码语言:javascript复制
 1// SnowFlake.h
 2
 3#pragma once
 4
 5#include <mutex>
 6#include <chrono>
 7#include <stdint.h>
 8
 9extern const uint64_t INVALID_ID = 0;
10
11class SnowFlake
12{
13public:
14    SnowFlake(uint64_t datacenterId, uint64_t machineId, 
15        uint64_t datacenterIdBit = 5, uint64_t machineIdBit = 5, uint64_t sequenceBit = 12)
16        :   datacenterIdBit_(datacenterIdBit),
17            machineIdBit_(machineIdBit),
18            sequenceBit_(sequenceBit),
19            maxDataCenterCount_(-1 ^ (uint64_t(-1) << datacenterIdBit_)),
20            maxMachineCount_(-1 ^ (uint64_t(-1) << machineIdBit)),
21            maxSequenceCount_(-1 ^ (uint64_t(-1) << sequenceBit_)),
22            machineShift_(sequenceBit_),
23            dataCenterShift_(machineShift_   machineIdBit_),
24            timestampShift_(dataCenterShift_   datacenterIdBit_),
25            datacenterId_(datacenterId),
26            machineId_(machineId)            
27    {
28        if (datacenterIdBit   machineIdBit   sequenceBit != 64 - 1 - 41)
29        {
30            std::cout << "datacenterIdBit   machineIdBit   sequenceBit != 22, please check." << std::endl;
31            exit(0);
32        }
33    }
34    ~SnowFlake() {}
35
36    uint64_t nextId() {
37        std::unique_lock<std::mutex> lock(mutex_);
38        uint64_t currTimestsmp = timeGen();
39        if (currTimestsmp < lastTimestamp_) {
40            std::cout << "Clock has been moved backwards. Generate id failed." << std::endl;
41            return INVALID_ID;
42        }
43
44        if (currTimestsmp == lastTimestamp_) {
45            sequence_ = (sequence_   1) & maxSequenceCount_;
46            if (0 == sequence_) {
47                currTimestsmp = tilNextMillis();
48            }
49        }
50        else {
51            sequence_ = 0;
52        }
53
54        lastTimestamp_ = currTimestsmp;
55        return lastTimestamp_ << timestampShift_
56            | datacenterId_ << dataCenterShift_
57            | machineShift_ << machineShift_
58            | sequence_;
59    }
60
61protected:
62    uint64_t timeGen() const {
63        return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() - startTimestamp_;
64    }
65
66    uint64_t tilNextMillis() const {
67        uint64_t mill = timeGen();
68        while (mill <= lastTimestamp_) {
69            mill = timeGen();
70        }
71        return mill;
72    }
73
74private:
75    const uint64_t startTimestamp_ = 1577836800000;    // 2020/01/01 08:00:00
76    const uint64_t datacenterIdBit_;    
77    const uint64_t machineIdBit_;
78    const uint64_t sequenceBit_;
79
80    const uint64_t maxMachineCount_;
81    const uint64_t maxDataCenterCount_;
82    const uint64_t maxSequenceCount_;
83
84    const uint64_t machineShift_;
85    const uint64_t dataCenterShift_;
86    const uint64_t timestampShift_;
87
88    uint64_t lastTimestamp_ = 0;
89    uint64_t datacenterId_  = 0;
90    uint64_t machineId_     = 0;
91    uint64_t sequence_      = 0;
92
93    std::mutex mutex_;
94};

2.4 测试程序

该测试程序测试每秒生成 ID 的数量,基本上每秒可以生成 900000~1100000 左右:

代码语言:javascript复制
 1#include <iostream>
 2
 3#include "SnowFlake.h"
 4
 5int main()
 6{
 7    std::chrono::steady_clock::time_point start_;
 8    std::chrono::steady_clock::time_point end_;
 9
10    SnowFlake uuid(5 , 10);
11    start_ = std::chrono::steady_clock::now();
12    uint64_t cnt = 0;
13
14    while (std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start_).count() < 1000)
15    {
16        cnt  ;
17        uuid.nextId();
18    }
19
20    std::cout << "SnowFlake generates " << cnt << " ids one second." << std::endl;
21}

0 人点赞