本文目录导读:

我来为您提供一个PHP实现的雪花算法(Snowflake ID)生成器,这是一个分布式ID生成方案,能够生成全局唯一的64位整数ID。
基础版雪花算法实现
<?php
class Snowflake
{
// 各个部分的位数
const EPOCH_BITS = 41; // 时间戳位数
const MACHINE_BITS = 10; // 机器ID位数
const SEQUENCE_BITS = 12; // 序列号位数
// 最大支持的值
const MAX_MACHINE_ID = -1 ^ (-1 << self::MACHINE_BITS); // 1023
const MAX_SEQUENCE = -1 ^ (-1 << self::SEQUENCE_BITS); // 4095
// 偏移量
const MACHINE_SHIFT = self::SEQUENCE_BITS; // 12
const TIMESTAMP_SHIFT = self::SEQUENCE_BITS + self::MACHINE_BITS; // 22
// 起始时间戳(2020-01-01)
private const EPOCH = 1577808000000;
private $machineId; // 机器ID
private $sequence = 0; // 序列号
private $lastTimestamp = -1; // 上次生成ID的时间戳
/**
* 构造函数
* @param int $machineId 机器ID (0-1023)
* @throws Exception
*/
public function __construct($machineId = 0)
{
if ($machineId < 0 || $machineId > self::MAX_MACHINE_ID) {
throw new Exception("Machine ID must be between 0 and " . self::MAX_MACHINE_ID);
}
$this->machineId = $machineId;
}
/**
* 生成新的ID
* @return int 64位雪花算法ID
* @throws Exception
*/
public function nextId()
{
$timestamp = $this->getCurrentTimestamp();
// 处理时钟回拨
if ($timestamp < $this->lastTimestamp) {
throw new Exception("Clock moved backwards. Refusing to generate id for " .
($this->lastTimestamp - $timestamp) . " milliseconds");
}
// 同一毫秒内生成ID
if ($timestamp === $this->lastTimestamp) {
$this->sequence = ($this->sequence + 1) & self::MAX_SEQUENCE;
// 序列号用尽,等待下一毫秒
if ($this->sequence === 0) {
$timestamp = $this->waitNextMillis($this->lastTimestamp);
}
} else {
// 新的毫秒,序列号从0开始
$this->sequence = 0;
}
$this->lastTimestamp = $timestamp;
// 组合生成ID
return (($timestamp - self::EPOCH) << self::TIMESTAMP_SHIFT) |
($this->machineId << self::MACHINE_SHIFT) |
$this->sequence;
}
/**
* 等待到下一毫秒
*/
private function waitNextMillis($lastTimestamp)
{
$timestamp = $this->getCurrentTimestamp();
while ($timestamp <= $lastTimestamp) {
usleep(100); // 等待100微秒
$timestamp = $this->getCurrentTimestamp();
}
return $timestamp;
}
/**
* 获取当前毫秒时间戳
*/
private function getCurrentTimestamp()
{
return (int)(microtime(true) * 1000);
}
/**
* 解析ID,获取其组成部分(用于调试)
*/
public function parseId($id)
{
$timestamp = ($id >> self::TIMESTAMP_SHIFT) + self::EPOCH;
$machineId = ($id >> self::MACHINE_SHIFT) & self::MAX_MACHINE_ID;
$sequence = $id & self::MAX_SEQUENCE;
return [
'timestamp' => $timestamp,
'datetime' => date('Y-m-d H:i:s', $timestamp / 1000),
'machine_id' => $machineId,
'sequence' => $sequence
];
}
}
// 使用示例
try {
$snowflake = new Snowflake(1); // 机器ID为1
// 生成多个ID
$ids = [];
for ($i = 0; $i < 10; $i++) {
$ids[] = $snowflake->nextId();
}
print_r($ids);
// 解析第一个ID
print_r($snowflake->parseId($ids[0]));
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
改进版:支持更灵活的配置
<?php
class FlexibleSnowflake
{
// 默认配置(Twitter Snowflake标准)
private $config = [
'epoch' => 1577808000000, // 起始时间
'timestamp_bits' => 41, // 时间戳位数
'machine_bits' => 10, // 机器ID位数
'sequence_bits' => 12, // 序列号位数
];
private $machineId;
private $sequence = 0;
private $lastTimestamp = -1;
// 计算后的值
private $maxMachineId;
private $maxSequence;
private $machineShift;
private $timestampShift;
public function __construct($machineId = 0, $config = [])
{
// 合并配置
$this->config = array_merge($this->config, $config);
// 计算位偏移
$this->machineShift = $this->config['sequence_bits'];
$this->timestampShift = $this->config['sequence_bits'] + $this->config['machine_bits'];
// 计算最大值
$this->maxMachineId = -1 ^ (-1 << $this->config['machine_bits']);
$this->maxSequence = -1 ^ (-1 << $this->config['sequence_bits']);
// 验证机器ID
if ($machineId < 0 || $machineId > $this->maxMachineId) {
throw new Exception("Machine ID must be between 0 and " . $this->maxMachineId);
}
$this->machineId = $machineId;
}
public function nextId()
{
$timestamp = $this->getTimestamp();
if ($timestamp < $this->lastTimestamp) {
throw new Exception("Clock moved backwards");
}
if ($timestamp === $this->lastTimestamp) {
$this->sequence = ($this->sequence + 1) & $this->maxSequence;
if ($this->sequence === 0) {
$timestamp = $this->waitNextMillis();
}
} else {
$this->sequence = 0;
}
$this->lastTimestamp = $timestamp;
return (($timestamp - $this->config['epoch']) << $this->timestampShift) |
($this->machineId << $this->machineShift) |
$this->sequence;
}
private function getTimestamp()
{
return (int)(microtime(true) * 1000);
}
private function waitNextMillis()
{
$timestamp = $this->getTimestamp();
while ($timestamp <= $this->lastTimestamp) {
usleep(100);
$timestamp = $this->getTimestamp();
}
return $timestamp;
}
}
带缓存和锁的线程安全版本
<?php
class ThreadSafeSnowflake extends Snowflake
{
private $lockFile;
private $fp;
public function __construct($machineId = 0, $lockPath = '/tmp/')
{
parent::__construct($machineId);
// 创建锁文件(用于多进程/多线程场景)
$this->lockFile = $lockPath . 'snowflake_' . $machineId . '.lock';
$this->fp = fopen($this->lockFile, 'c');
if (!$this->fp) {
throw new Exception("Cannot open lock file");
}
}
public function nextId()
{
// 文件锁确保并发安全
if (!flock($this->fp, LOCK_EX)) {
throw new Exception("Cannot acquire lock");
}
try {
return parent::nextId();
} finally {
flock($this->fp, LOCK_UN);
}
}
public function __destruct()
{
if ($this->fp) {
fclose($this->fp);
}
}
}
批量生成ID(性能优化)
<?php
class BatchSnowflake extends Snowflake
{
/**
* 批量生成ID
* @param int $batchSize 批量大小
* @return array
*/
public function nextBatchId($batchSize = 100)
{
$ids = [];
for ($i = 0; $i < $batchSize; $i++) {
$ids[] = $this->nextId();
}
return $ids;
}
/**
* 预生成一批ID并缓存
*/
private $idCache = [];
public function getCachedId()
{
if (empty($this->idCache)) {
$this->idCache = $this->nextBatchId(100);
}
return array_shift($this->idCache);
}
}
使用建议
- 机器ID分配:在多服务器环境中,每个节点使用不同的machineId
- 时钟同步:确保服务器时钟差异小于10ms
- 并发控制:高并发场景使用锁或Redis原子操作
- ID格式:返回的ID可以作为字符串处理,避免PHP整数溢出
测试示例
// 测试代码
$snowflake = new Snowflake(1);
$startTime = microtime(true);
$testCount = 10000;
for ($i = 0; $i < $testCount; $i++) {
$id = $snowflake->nextId();
if ($i < 5) {
echo "ID: " . $id . "\n";
}
}
$endTime = microtime(true);
$timeUsed = $endTime - $startTime;
echo "生成 " . $testCount . " 个ID耗时: " . $timeUsed . " 秒\n";
echo "平均速度: " . ($testCount / $timeUsed) . " IDs/秒\n";
echo "内存占用: " . memory_get_peak_usage(true) / 1024 . " KB\n";
这个实现包含了完整的雪花算法核心功能,你可以根据实际需求选择使用基础版或功能增强版,记得根据你的PHP环境(32位还是64位)进行相应调整。