雪花算法(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
代码说明:
- EPOCH:起始时间戳,可以根据项目启动时间设置。
- MACHINE_BITS 和 DATACENTER_BITS:分别表示机器 ID 和数据中心 ID 的位数。
- SEQUENCE_BITS:序列号的位数。
- MAX_MACHINE_ID 和 MAX_DATACENTER_ID:根据位数计算出的最大机器 ID 和最大数据中心 ID。
- nextId() :生成唯一 ID 的核心方法。
- timestamp() :获取当前时间戳(毫秒)。
- tilNextMillis() :如果当前时间戳与上次生成 ID 的时间戳相同,则等待直到下一个毫秒。
使用示例:
$snowflake = new Snowflake(1, 1);
echo $snowflake->nextId(); // 输出生成的唯一 ID
注意事项:
- 机器 ID 和数据中心 ID 需要在分布式系统中唯一分配。
- 如果系统时钟回拨,可能会导致 ID 重复,因此需要处理时钟回拨的情况。
通过这种方式,你可以在 PHP 中实现雪花算法来生成全局唯一的 ID。