分布式系统通过雪花算法生成唯一ID

服务器系列



前言

在分布式系统中,经常涉及到时间同步问题,这样由于时间校准,以及其他因素,可能导致服务器时间回退,如果恰巧回退前生成过一些id,而时间回退后,生成的id就有可能重复。现有
技术中对于此问题并没有给出明确的解决方案,而是简单的抛错处理,这样会造成在时间
被追回之前的这段时间服务不可用,导致交易失败,用户体验极其不好。


一、雪花算法

雪花算法是一种用于生成唯一ID的算法,它的核心思想是将时间戳、机器ID和序列号组合起来生成一个唯一的ID。具体实现过程如下:

获取当前时间戳,精确到毫秒级别。
将时间戳转换成二进制,并左移22位,腾出前22位用于存储机器ID。
获取机器ID,可以是MAC地址、IP地址或者自定义的ID。
将机器ID转换成二进制,并左移12位,腾出中间的12位用于存储序列号。
生成序列号,可以是自增的数字或者随机数。
将时间戳、机器ID和序列号进行位运算或者相加,生成一个64位的唯一ID。

二、C++的代码示例

#include <iostream>
#include <chrono>
#include <thread>

class Snowflake {
public:
    Snowflake(uint16_t worker_id) : worker_id_(worker_id), sequence_(0) {}

    uint64_t next_id() {
        uint64_t timestamp = get_timestamp();
        uint64_t id = ((timestamp - epoch_) << timestamp_left_shift_) |
                      (worker_id_ << worker_id_left_shift_) |
                      (sequence_++ & sequence_mask_);
        if (sequence_ > sequence_mask_) {
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
            sequence_ = 0;
        }
        return id;
    }

private:
    uint64_t get_timestamp() {
        return std::chrono::duration_cast<std::chrono::milliseconds>(
                std::chrono::system_clock::now().time_since_epoch()).count();
    }

    const uint64_t epoch_ = 1609459200000; // 2021-01-01 00:00:00
    const uint64_t worker_id_left_shift_ = 12;
    const uint64_t timestamp_left_shift_ = 22;
    const uint64_t sequence_mask_ = 0xFFF;
    uint16_t worker_id_;
    uint64_t sequence_;
};

int main() {
    Snowflake snowflake(1);
    for (int i = 0; i < 10; i++) {
        std::cout << snowflake.next_id() << std::endl;
    }
    return 0;
}

总结

以上代码中,Snowflake类表示雪花算法,next_id()函数用于生成唯一ID。在构造函数中传入worker_id,表示机器ID。get_timestamp()函数用于获取当前时间戳,精确到毫秒级别。在next_id()函数中,根据时间戳、机器ID和序列号生成唯一ID,并将序列号自增,如果序列号超过了最大值,则等待1毫秒后重置序列号。最后输出生成的唯一ID。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
雪花算法是种生成分布式ID的算法,它可以生成一个64位的ID,其中包含了时间戳、数据中心ID和机器ID等信息。下面是雪花算法生成分布式ID的软件设计模型: 1. 定义一个Snowflake类,该类包含以下属性: - datacenter_id: 数据中心ID,占5位,取值围为0~31。 - worker_id: 机器ID,占5位,取值范围为0~31。 - sequence: 序列号,占12位,取值范围为0~4095。 - last_timestamp: 上一次生成ID的时间戳。 2. 实现Snowflake类的构造函数,初始化datacenter_id和worker_id属性。 3. 实现一个next_id方法,该方法用于生成下一个ID。具体实现如下: - 获取当前时间戳,单位为毫秒。 - 如果当前时间戳小于上一次生成ID的时间戳,则说明系统时钟回退过,抛出异常。 - 如果当前时间戳等于上一次生成ID的时间戳,则将序列号加1。 - 如果当前时间戳大于上一次生成ID的时间戳,则将序列号重置为0,并将last_timestamp属性更新为当前时间戳。 - 将datacenter_id、worker_id、时间戳和序列号按照一定的位数组合成一个64位的ID。 - 返回生成ID。 4. 在分布式系统中,每个节点都需要创建一个Snowflake实例,并指定不同的datacenter_id和worker_id。每个节点生成ID都是唯一的,且具有时间顺序。 下面是一个Python实现的雪花算法生成分布式ID的代码示例: ```python import time class Snowflake: def __init__(self, datacenter_id, worker_id): self.datacenter_id = datacenter_id self.worker_id = worker_id self.sequence = 0 self.last_timestamp = -1 def next_id(self): timestamp = int(time.time() * 1000) if timestamp < self.last_timestamp: raise Exception("Clock moved backwards. Refusing to generate id") if timestamp == self.last_timestamp: self.sequence = (self.sequence + 1) & 4095 if self.sequence == 0: timestamp = self.wait_next_millis(self.last_timestamp) else: self.sequence = 0 self.last_timestamp = timestamp return ((timestamp - 1288834974657) << 22) | (self.datacenter_id << 17) | (self.worker_id << 12) | self.sequence def wait_next_millis(self, last_timestamp): timestamp = int(time.time() * 1000) while timestamp <= last_timestamp: timestamp = int(time.time() * 1000) return timestamp ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值