php 雪花算法 + 10 62进制转换

179 阅读1分钟

雪花算法

<?php

class Snowflake
{
    private $epoch; // 纪元(起始时间戳),以毫秒为单位
    private $datacenterId; // 数据中心ID
    private $workerId; // 工作节点ID
    private $sequence; // 序列号

    private $datacenterIdBits = 5; // 数据中心ID的位数
    private $workerIdBits = 5; // 工作节点ID的位数
    private $sequenceBits = 12; // 序列号的位数
    private $timestampBits = 41; // 序列号的位数

    private $maxDatacenterId; // 最大数据中心ID(计算得出)
    private $maxWorkerId; // 最大工作节点ID(计算得出)
    private $maxSequence; // 最大序列号(计算得出)

    private $workerIdShift; // 工作节点ID的位移量
    private $datacenterIdShift; // 数据中心ID的位移量
    private $timestampLeftShift; // 时间戳的位移量

    private $lastTimestamp = -1; // 上次生成ID的时间戳

    public function __construct(int $datacenterId, int $workerId, int $epoch = 1695030884971)
    {
        // 计算最大数据中心ID、工作节点ID和序列号
        $this->maxDatacenterId = -1 ^ (-1 << $this->datacenterIdBits);
        $this->maxWorkerId = -1 ^ (-1 << $this->workerIdBits);
        $this->maxSequence = -1 ^ (-1 << $this->sequenceBits);

        // 检查数据中心ID和工作节点ID是否超出范围
        if ($datacenterId > $this->maxDatacenterId || $datacenterId < 0) {
            throw new \Exception("Datacenter ID超出范围");
        }
        if ($workerId > $this->maxWorkerId || $workerId < 0) {
            throw new \Exception("Worker ID超出范围");
        }

        // 设置工作节点ID和数据中心ID的位移量
        $this->workerIdShift = $this->sequenceBits;
        $this->datacenterIdShift = $this->sequenceBits + $this->workerIdBits;
        $this->timestampLeftShift = $this->timestampBits;

        // 设置数据中心ID和工作节点ID
        $this->datacenterId = $datacenterId;
        $this->workerId = $workerId;
        $this->epoch = $epoch;
    }

    public function generateId(): int
    {
        $timestamp = $this->getTimestamp();

        // 如果当前时间小于上次生成ID的时间戳,说明系统时钟回退过
        if ($timestamp < $this->lastTimestamp) {
            throw new \Exception("系统时钟发生回退");
        }

        // 如果是同一时间生成的,递增序列号
        if ($timestamp == $this->lastTimestamp) {
            $this->sequence = ($this->sequence + 1) & $this->maxSequence;
            if ($this->sequence === 0) {
                // 序列号已经达到最大值,等待下一毫秒
                $timestamp = $this->waitNextMillis($this->lastTimestamp);
            }
        } else {
            // 不同时间,序列号重置为0
            $this->sequence = 0;
        }

        // 更新上次生成ID的时间戳
        $this->lastTimestamp = $timestamp;

        // 生成ID
        $id = (($timestamp - $this->epoch) << $this->timestampLeftShift) |
            ($this->datacenterId << $this->datacenterIdShift) |
            ($this->workerId << $this->workerIdShift) |
            $this->sequence;

        return $id;
    }

    private function getTimestamp(): int
    {
        // 获取当前时间戳(毫秒)
        return (int) floor(microtime(true) * 1000);
    }

    private function waitNextMillis($lastTimestamp): int
    {
        $timestamp = $this->getTimestamp();
        while ($timestamp <= $lastTimestamp) {
            $timestamp = $this->getTimestamp();
        }
        return $timestamp;
    }
}

10进制转62进制

public function decimalToBase62($decimal) {
    $base62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    $base62_length = strlen($base62);
    $result = '';

    while ($decimal > 0) {
        $remainder = $decimal % $base62_length;
        $result .= $base62[$remainder];
        $decimal = intdiv($decimal, $base62_length);
    }

    return strrev($result);
}

62进制转10进制

public function base62ToDecimal($number) {
    $base62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    $base62_length = strlen($base62);
    $number = strrev($number);
    $dec = 0;

    for ($i = 0; $i < strlen($number); $i++) {
        $digit = strpos($base62, $number[$i]);
        $dec += $digit * pow($base62_length, $i);
    }

    return $dec;
}