2025-12-26 11:47:14 +08:00
|
|
|
|
<?php
|
2025-12-26 18:46:06 +08:00
|
|
|
|
|
2025-12-26 11:47:14 +08:00
|
|
|
|
namespace app\common\library;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 雪花算法ID生成器
|
2025-12-26 18:46:06 +08:00
|
|
|
|
* 不依赖数据库,通过配置或自动生成workerId
|
|
|
|
|
|
* 64位ID结构:0(1位符号位) + 41位时间戳 + 10位工作机器ID + 12位序列号
|
2025-12-26 11:47:14 +08:00
|
|
|
|
*/
|
|
|
|
|
|
class Snowflake
|
|
|
|
|
|
{
|
2025-12-26 18:46:06 +08:00
|
|
|
|
// 起始时间戳(2023-01-01 00:00:00),可使用到2089年
|
2025-12-26 11:47:14 +08:00
|
|
|
|
const EPOCH = 1672531200000;
|
|
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
// 工作机器ID位数
|
|
|
|
|
|
const WORKER_ID_BITS = 10;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
|
|
|
|
|
// 序列号位数
|
|
|
|
|
|
const SEQUENCE_BITS = 12;
|
|
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
// 最大工作机器ID
|
|
|
|
|
|
const MAX_WORKER_ID = (1 << self::WORKER_ID_BITS) - 1;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
// 序列号掩码
|
|
|
|
|
|
const SEQUENCE_MASK = (1 << self::SEQUENCE_BITS) - 1;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
// 工作机器ID左移位数
|
|
|
|
|
|
const WORKER_ID_SHIFT = self::SEQUENCE_BITS;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
|
|
|
|
|
// 时间戳左移位数
|
2025-12-26 18:46:06 +08:00
|
|
|
|
const TIMESTAMP_LEFT_SHIFT = self::SEQUENCE_BITS + self::WORKER_ID_BITS;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
// 工作机器ID(通过服务器IP生成)
|
|
|
|
|
|
protected $workerId;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
|
|
|
|
|
// 序列号
|
2025-12-26 18:46:06 +08:00
|
|
|
|
protected $sequence = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 上次生成ID的时间戳
|
|
|
|
|
|
protected $lastTimestamp = -1;
|
|
|
|
|
|
|
|
|
|
|
|
// 单例实例
|
|
|
|
|
|
protected static $instance = null;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构造函数私有化,单例模式
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function __construct()
|
|
|
|
|
|
{
|
|
|
|
|
|
$this->workerId = $this->generateWorkerId();
|
|
|
|
|
|
}
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-26 18:46:06 +08:00
|
|
|
|
* 获取单例实例
|
2025-12-26 11:47:14 +08:00
|
|
|
|
*/
|
2025-12-26 18:46:06 +08:00
|
|
|
|
public static function getInstance()
|
2025-12-26 11:47:14 +08:00
|
|
|
|
{
|
2025-12-26 18:46:06 +08:00
|
|
|
|
if (self::$instance === null) {
|
|
|
|
|
|
self::$instance = new self();
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
2025-12-26 18:46:06 +08:00
|
|
|
|
return self::$instance;
|
|
|
|
|
|
}
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 生成工作机器ID(基于服务器IP)
|
|
|
|
|
|
*/
|
|
|
|
|
|
protected function generateWorkerId()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 方法1:从配置文件中读取
|
|
|
|
|
|
if (config('snowflake.worker_id')) {
|
|
|
|
|
|
return config('snowflake.worker_id') & self::MAX_WORKER_ID;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 方法2:基于服务器IP生成(推荐)
|
|
|
|
|
|
$serverIp = $_SERVER['SERVER_ADDR'] ?? '127.0.0.1';
|
|
|
|
|
|
$ipParts = explode('.', $serverIp);
|
|
|
|
|
|
|
|
|
|
|
|
// 使用IP后两段生成workerId
|
|
|
|
|
|
if (count($ipParts) >= 4) {
|
|
|
|
|
|
$workerId = ($ipParts[2] << 8) | $ipParts[3];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 如果是IPv6或特殊情况,使用随机数
|
|
|
|
|
|
$workerId = mt_rand(0, self::MAX_WORKER_ID);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $workerId & self::MAX_WORKER_ID;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-26 18:46:06 +08:00
|
|
|
|
* 生成下一个ID
|
2025-12-26 11:47:14 +08:00
|
|
|
|
*/
|
2025-12-26 18:46:06 +08:00
|
|
|
|
public function nextId()
|
2025-12-26 11:47:14 +08:00
|
|
|
|
{
|
2025-12-26 18:46:06 +08:00
|
|
|
|
$timestamp = $this->getCurrentTimestamp();
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
// 时钟回拨处理
|
2025-12-26 11:47:14 +08:00
|
|
|
|
if ($timestamp < $this->lastTimestamp) {
|
2025-12-26 18:46:06 +08:00
|
|
|
|
$diff = $this->lastTimestamp - $timestamp;
|
|
|
|
|
|
throw new \Exception("时钟回拨了 {$diff} 毫秒");
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
// 同一毫秒内生成多个ID
|
|
|
|
|
|
if ($this->lastTimestamp == $timestamp) {
|
|
|
|
|
|
$this->sequence = ($this->sequence + 1) & self::SEQUENCE_MASK;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
if ($this->sequence == 0) {
|
2025-12-26 18:46:06 +08:00
|
|
|
|
// 序列号用尽,等待下一毫秒
|
2025-12-26 11:47:14 +08:00
|
|
|
|
$timestamp = $this->waitNextMillis($this->lastTimestamp);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$this->sequence = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$this->lastTimestamp = $timestamp;
|
|
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
// 组合生成ID
|
|
|
|
|
|
$id = (($timestamp - self::EPOCH) << self::TIMESTAMP_LEFT_SHIFT)
|
|
|
|
|
|
| ($this->workerId << self::WORKER_ID_SHIFT)
|
|
|
|
|
|
| $this->sequence;
|
|
|
|
|
|
|
|
|
|
|
|
return (string)$id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量生成ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function nextIds($count)
|
|
|
|
|
|
{
|
|
|
|
|
|
$ids = [];
|
|
|
|
|
|
for ($i = 0; $i < $count; $i++) {
|
|
|
|
|
|
$ids[] = $this->nextId();
|
|
|
|
|
|
}
|
|
|
|
|
|
return $ids;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前毫秒时间戳
|
|
|
|
|
|
*/
|
2025-12-26 18:46:06 +08:00
|
|
|
|
protected function getCurrentTimestamp()
|
2025-12-26 11:47:14 +08:00
|
|
|
|
{
|
|
|
|
|
|
return (int)(microtime(true) * 1000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-26 18:46:06 +08:00
|
|
|
|
* 等待到下一毫秒
|
2025-12-26 11:47:14 +08:00
|
|
|
|
*/
|
2025-12-26 18:46:06 +08:00
|
|
|
|
protected function waitNextMillis($lastTimestamp)
|
2025-12-26 11:47:14 +08:00
|
|
|
|
{
|
2025-12-26 18:46:06 +08:00
|
|
|
|
$timestamp = $this->getCurrentTimestamp();
|
2025-12-26 11:47:14 +08:00
|
|
|
|
while ($timestamp <= $lastTimestamp) {
|
|
|
|
|
|
usleep(100); // 休眠100微秒
|
2025-12-26 18:46:06 +08:00
|
|
|
|
$timestamp = $this->getCurrentTimestamp();
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
return $timestamp;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-26 18:46:06 +08:00
|
|
|
|
* 解析雪花ID
|
2025-12-26 11:47:14 +08:00
|
|
|
|
*/
|
2025-12-26 18:46:06 +08:00
|
|
|
|
public static function parse($id)
|
2025-12-26 11:47:14 +08:00
|
|
|
|
{
|
2025-12-26 18:46:06 +08:00
|
|
|
|
$id = intval($id);
|
2025-12-26 11:47:14 +08:00
|
|
|
|
return [
|
2025-12-26 18:46:06 +08:00
|
|
|
|
'timestamp' => ($id >> self::TIMESTAMP_LEFT_SHIFT) + self::EPOCH,
|
|
|
|
|
|
'worker_id' => ($id >> self::WORKER_ID_SHIFT) & self::MAX_WORKER_ID,
|
|
|
|
|
|
'sequence' => $id & self::SEQUENCE_MASK,
|
|
|
|
|
|
'generated_at' => date('Y-m-d H:i:s.v', (($id >> self::TIMESTAMP_LEFT_SHIFT) + self::EPOCH) / 1000)
|
2025-12-26 11:47:14 +08:00
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|