用deepseek写了一个雪花算法生成唯一ID

601 阅读2分钟

雪花算法(Snowflake Algorithm)是 Twitter 开源的一种分布式 ID 生成算法,用于生成全局唯一的 ID。它的核心思想是将一个 64 位的 ID 分成多个部分,分别表示时间戳、机器 ID、数据中心 ID 和序列号。通过这种方式,可以在分布式系统中生成唯一的 ID。

在 PHP 中实现雪花算法可以参考以下代码:

<?php

class Snowflake
{
    // 起始时间戳(毫秒),可以设置为项目启动时间
    private const EPOCH = 1609459200000; // 2021-01-01 00:00:00

    // 机器 ID 位数
    private const MACHINE_BITS = 5;

    // 数据中心 ID 位数
    private const DATACENTER_BITS = 5;

    // 序列号位数
    private const SEQUENCE_BITS = 12;

    // 最大机器 ID
    private const MAX_MACHINE_ID = (-1 ^ (-1 << self::MACHINE_BITS));

    // 最大数据中心 ID
    private const MAX_DATACENTER_ID = (-1 ^ (-1 << self::DATACENTER_BITS));

    // 机器 ID 左移位数
    private const MACHINE_SHIFT = self::SEQUENCE_BITS;

    // 数据中心 ID 左移位数
    private const DATACENTER_SHIFT = self::SEQUENCE_BITS + self::MACHINE_BITS;

    // 时间戳左移位数
    private const TIMESTAMP_SHIFT = self::SEQUENCE_BITS + self::MACHINE_BITS + self::DATACENTER_BITS;

    // 序列号掩码
    private const SEQUENCE_MASK = (-1 ^ (-1 << self::SEQUENCE_BITS));

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

    // 序列号
    private $sequence = 0;

    // 机器 ID
    private $machineId;

    // 数据中心 ID
    private $datacenterId;

    public function __construct(int $machineId, int $datacenterId)
    {
        if ($machineId > self::MAX_MACHINE_ID || $machineId < 0) {
            throw new \InvalidArgumentException("Machine ID must be between 0 and " . self::MAX_MACHINE_ID);
        }

        if ($datacenterId > self::MAX_DATACENTER_ID || $datacenterId < 0) {
            throw new \InvalidArgumentException("Datacenter ID must be between 0 and " . self::MAX_DATACENTER_ID);
        }

        $this->machineId = $machineId;
        $this->datacenterId = $datacenterId;
    }

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

        if ($timestamp < $this->lastTimestamp) {
            throw new \RuntimeException("Clock moved backwards. Refusing to generate id for " . ($this->lastTimestamp - $timestamp) . " milliseconds");
        }

        if ($this->lastTimestamp == $timestamp) {
            $this->sequence = ($this->sequence + 1) & self::SEQUENCE_MASK;
            if ($this->sequence == 0) {
                $timestamp = $this->tilNextMillis($this->lastTimestamp);
            }
        } else {
            $this->sequence = 0;
        }

        $this->lastTimestamp = $timestamp;

        return (($timestamp - self::EPOCH) << self::TIMESTAMP_SHIFT) |
            ($this->datacenterId << self::DATACENTER_SHIFT) |
            ($this->machineId << self::MACHINE_SHIFT) |
            $this->sequence;
    }

    private function timestamp(): int
    {
        return (int)(microtime(true) * 1000);
    }

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

// 使用示例
$snowflake = new Snowflake(1, 1);
echo $snowflake->nextId(); // 输出生成的唯一 ID

代码说明:

  1. EPOCH:起始时间戳,可以根据项目启动时间设置。
  2. MACHINE_BITS 和 DATACENTER_BITS:分别表示机器 ID 和数据中心 ID 的位数。
  3. SEQUENCE_BITS:序列号的位数。
  4. MAX_MACHINE_ID 和 MAX_DATACENTER_ID:根据位数计算出的最大机器 ID 和最大数据中心 ID。
  5. nextId() :生成唯一 ID 的核心方法。
  6. timestamp() :获取当前时间戳(毫秒)。
  7. tilNextMillis() :如果当前时间戳与上次生成 ID 的时间戳相同,则等待直到下一个毫秒。

使用示例:

$snowflake = new Snowflake(1, 1);
echo $snowflake->nextId(); // 输出生成的唯一 ID

注意事项:

  • 机器 ID 和数据中心 ID 需要在分布式系统中唯一分配。
  • 如果系统时钟回拨,可能会导致 ID 重复,因此需要处理时钟回拨的情况。

通过这种方式,你可以在 PHP 中实现雪花算法来生成全局唯一的 ID。