本文目录导读:

在PHP分布式系统中生成唯一ID,常见方案如下,我会从实现复杂度和性能两个维度帮你分析:
UUID(最简单)
// 生成36位字符串:550e8400-e29b-41d4-a716-446655440000 $uuid = bin2hex(random_bytes(16)); // 32位无连字符 // 或使用ramsey/uuid包 use Ramsey\Uuid\Uuid; $uuid = Uuid::uuid4()->toString();
特点:
- ✅ 无中心化,完全本地生成
- ❌ 长度36位,不适合做数据库主键(影响索引性能)
- ❌ 无序,B+树插入效率低
雪花算法(推荐)
Twitter开源的经典方案,PHP实现:
class Snowflake {
const EPOCH = 1609459200000; // 2021-01-01 00:00:00 毫秒
private $workerId;
private $lastTimestamp = -1;
private $sequence = 0;
public function __construct($workerId = 0) {
$this->workerId = $workerId;
}
public function nextId() {
$timestamp = $this->getTimestamp();
if ($timestamp < $this->lastTimestamp) {
throw new \Exception('时钟回拨');
}
if ($timestamp == $this->lastTimestamp) {
$this->sequence = ($this->sequence + 1) & 0xFFF;
if ($this->sequence == 0) {
$timestamp = $this->waitNextMs($this->lastTimestamp);
}
} else {
$this->sequence = 0;
}
$this->lastTimestamp = $timestamp;
return (($timestamp - self::EPOCH) << 22)
| ($this->workerId << 12)
| $this->sequence;
}
private function getTimestamp() {
return intval(microtime(true) * 1000);
}
private function waitNextMs($lastTimestamp) {
$timestamp = $this->getTimestamp();
while ($timestamp <= $lastTimestamp) {
usleep(100);
$timestamp = $this->getTimestamp();
}
return $timestamp;
}
}
// 使用
$snowflake = new Snowflake($workerId);
$id = $snowflake->nextId(); // 纯数字,短且有序
特点:
- ✅ 64位整数,有序递增
- ✅ 高性能(PHP环境下每秒数万)
- ✅ 支持分布式(通过workerId区分节点)
Redis自增(适合高并发)
// 每天一个key,自增
$date = date('Ymd');
$seq = $redis->incr("order_id:$date");
$orderId = $date . str_pad($seq, 6, '0', STR_PAD_LEFT);
// 使用INCRBY批量获取
$ids = [];
$idRange = $redis->incrby('global_id', 10);
for ($i = $idRange - 9; $i <= $idRange; $i++) {
$ids[] = $id;
}
特点:
- ✅ 绝对唯一,可控制长度
- ❌ 依赖Redis,需要考虑高可用
- ✅ 适合秒杀等高并发场景
数据库号段模式(最稳妥)
// 数据库表结构
CREATE TABLE id_generator (
id INT PRIMARY KEY AUTO_INCREMENT,
`key` VARCHAR(50) UNIQUE,
max_id BIGINT NOT NULL DEFAULT 0,
step INT NOT NULL DEFAULT 1000
);
// PHP获取号段
function getSegment($key) {
$db = getDb();
$db->beginTransaction();
$stmt = $db->query("SELECT max_id, step FROM id_generator WHERE `key`='$key' FOR UPDATE");
$row = $stmt->fetch();
$newMax = $row['max_id'] + $row['step'];
$db->query("UPDATE id_generator SET max_id=$newMax WHERE `key`='$key'");
$db->commit();
return range($row['max_id'] + 1, $newMax);
}
// 使用时
$ids = getSegment('order');
$id = array_shift($ids); // 内存中分配
特点:
- ✅ 不依赖第三方组件
- ✅ 批量获取,减少数据库访问
- ❌ 需要管理号段池
方案选择建议
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 订单号(需展示) | Redis自增 | 可以控制长度,如"20231201-000001" |
| 内部系统ID | 雪花算法 | 性能好,自增索引 |
| 文件/资源标识 | UUID | 无需中心化 |
| 大型分布式系统 | 号段模式 | 可控性强,无单点 |
实际项目常见做法:
- 订单类:
时间前缀 + Redis自增(20231201001) - 用户ID:
雪花算法 - 文件存储:
UUID或MD5 - 消息ID:
雪花算法
需要我帮你针对具体业务场景做方案设计吗?