PHP生成唯一编号避坑uniqid函数会重复
你想知道 PHP 的 uniqid() 函数生成的 ID 是否会重复,以及在什么场景下会重复、如何避免,对吧?
一、核心结论
uniqid() 有可能重复,并非绝对唯一,尤其是高并发/短时间内频繁调用的场景,重复概率会显著上升。
二、为什么会重复?先搞懂 uniqid() 的生成逻辑
uniqid() 的默认生成规则:
ID = 微秒级时间戳的16进制表示(默认13位) + 可选的随机数后缀核心依赖的是服务器的系统时钟和可选的随机数,这两个点都是重复的风险源:
1. 核心重复场景
| 场景 | 重复原因 | 重复概率 |
|---|---|---|
| 高并发调用 | 同一微秒内多次调用 uniqid()(默认无后缀),系统时钟精度不足(比如Windows仅15ms精度),导致生成相同ID | 高(并发>1000/秒时极易出现) |
| 分布式部署 | 多台服务器的系统时钟同步误差,或同一时刻调用,可能生成相同ID | 中 |
| 无随机数后缀 | 仅依赖时间戳,短时间内调用会生成连续ID,极端情况(时钟回拨/精度低)会重复 | 中高 |
| 服务器时钟回拨 | 服务器时间被手动调整/同步,导致时间戳倒退,可能生成历史已存在的ID | 低但致命 |
2. 官方参数的影响(uniqid($prefix, $more_entropy))
$prefix:前缀,仅用于区分不同业务,不提升唯一性;$more_entropy = true:在ID末尾追加随机数(约5位),能大幅降低重复概率,但仍无法完全杜绝。
三、验证:模拟重复场景(代码示例)
<?php
// 模拟高并发:同一微秒内调用1000次uniqid(无随机后缀)
$ids = [];
$repeatCount = 0;
// 循环调用,强制在同一微秒内执行(实际高并发会触发)
for ($i = 0; $i < 1000; $i++) {
$id = uniqid(); // 默认无后缀
if (in_array($id, $ids)) {
$repeatCount++;
echo "发现重复ID:{$id}\n";
}
$ids[] = $id;
}
echo "总调用次数:1000,重复次数:{$repeatCount}\n";
// 对比:开启more_entropy参数
$ids2 = [];
$repeatCount2 = 0;
for ($i = 0; $i < 1000; $i++) {
$id = uniqid('', true); // 追加随机数后缀
if (in_array($id, $ids2)) {
$repeatCount2++;
echo "开启随机后缀后重复ID:{$id}\n";
}
$ids2[] = $id;
}
echo "开启随机后缀后,总调用次数:1000,重复次数:{$repeatCount2}\n";测试结果:
- 无后缀:高概率出现重复(尤其是Windows/低精度时钟服务器);
- 有后缀:重复概率大幅降低,但极端并发下仍可能重复。
四、如何避免 uniqid() 重复?(按推荐度排序)
1. 推荐方案:组合多维度唯一标识
/**
* 生成高可靠唯一ID(基于uniqid,避免重复)
* @return string
*/
function generateUniqueId()
{
// 组合:前缀 + 微秒时间戳 + 随机数 + 进程ID + 唯一后缀
$prefix = 'ID';
$microtime = substr(microtime(true) * 1000000, -6); // 6位微秒
$random = mt_rand(100000, 999999); // 6位随机数
$pid = getmypid(); // 进程ID(分布式可加服务器IP/ID)
$uniq = uniqid('', true); // 带随机后缀的uniqid
return $prefix . $microtime . $random . $pid . $uniq;
}2. 进阶方案:使用PHP内置的加密级随机数
// 生成16位唯一ID(加密级随机,重复概率接近0)
$uniqueId = bin2hex(random_bytes(8)); // 8字节=16位16进制,密码学安全3. 分布式场景:雪花算法(Snowflake)
如果是多服务器/分布式系统,优先用雪花算法(生成64位整数ID),包含:
- 时间戳 + 机器ID + 进程ID + 序列号
完全杜绝重复,Yii/Laravel等框架都有现成扩展(如yii2-snowflake)。
五、uniqid() 的适用场景
| 场景 | 是否推荐用 uniqid() | 替代方案 |
|---|---|---|
| 低并发(<100/秒)、非核心业务(如临时文件名) | 推荐(加more_entropy) | - |
| 高并发、核心业务(订单号/团购编号) | 不推荐 | 雪花算法/组合唯一标识 |
| 分布式系统 | 不推荐 | 雪花算法/UUIDv4 |
总结
uniqid()不是绝对唯一:默认仅依赖微秒时间戳,高并发/时钟精度低时极易重复;- 开启
$more_entropy = true可大幅降低重复概率,但无法完全避免; - 核心业务/高并发场景:放弃纯
uniqid(),改用「时间戳+随机数+进程/机器ID」组合,或雪花算法/UUIDv4; - 低并发/非核心场景:
uniqid('', true)可满足基本需求。
版权属于:Joyber
本文链接:https://blog.qqvbc.com/default/1427.html
转载时须注明出处及本声明