The distributed system generates a unique ID through the snowflake algorithm

server series



foreword

In a distributed system, time synchronization issues are often involved, so due to time calibration and other factors, the server time may be rolled back. If some ids happen to be generated before the rollback, and after the time rolls back, the generated ids will have May be repeated. In the existing
technology, there is no clear solution to this problem, but simple error handling, which will cause the
service to be unavailable during the period before the time is recovered, resulting in transaction failure and extremely bad user experience .


1. Snowflake Algorithm

The snowflake algorithm is an algorithm for generating unique IDs. Its core idea is to combine timestamps, machine IDs, and serial numbers to generate a unique ID. The specific implementation process is as follows:

Get the current timestamp, accurate to the millisecond level.
Convert the timestamp to binary and shift it left by 22 bits to free up the first 22 bits for storing the machine ID.
Obtain the machine ID, which can be MAC address, IP address or a custom ID.
Convert the machine ID to binary and shift it left by 12 bits to free up the middle 12 bits for storing the serial number.
Generate a serial number, which can be an auto-incrementing number or a random number.
Perform bit operation or addition of timestamp, machine ID and serial number to generate a 64-bit unique ID.

Two, C++ code example

#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;
}

Summarize

In the above code, the Snowflake class represents the snowflake algorithm, and the next_id() function is used to generate a unique ID. Pass worker_id in the constructor, indicating the machine ID. The get_timestamp() function is used to obtain the current timestamp, accurate to the millisecond level. In the next_id() function, a unique ID is generated based on the timestamp, machine ID, and serial number, and the serial number is incremented. If the serial number exceeds the maximum value, the serial number is reset after waiting for 1 millisecond. Finally output the generated unique ID.

Guess you like

Origin blog.csdn.net/zyq880625/article/details/131126425