[分享]PHP中的uniqid在高并发下的重复问题

goosman 2010-11-05 02:49:00
项目是一个高并发的web项目,并且会有后台进程(pcntl并发),两者都会利用uniqid去生成唯一id,今天发现一个bug,在高并发情况下,uniqid可能产生重复输出。

以下是测试代码:

<?php
function new_child($func_name) {
$args = func_get_args();
unset($args[0]);
$pid = pcntl_fork();
if($pid == 0) {
function_exists($func_name) and exit(call_user_func_array($func_name, $args)) or exit(-1);
} else if($pid == -1) {
echo "Couldn’t create child process.";
} else {
return $pid;
}
}
function generate() {
$t = array();
while($i ++ < 10) {
$uid = uniqid(true)."\n";
array_push($t, $uid);
}
sort($t);
while(-- $i >=0) {
echo array_shift($t);
}
}
while($i ++ < 1000) {
new_child(generate);
}
?>


测试方法: 命令行运行此程序,重定向输出到文件,然后利用下面程序检查重复:

<?php
$f = file("tttttt");
$f = array_count_values($f);
foreach($f as $k => $c) if($c > 1) echo $c.'_'.$k;
?>

解决方法: 我们现在是在uniqid后又加了rand(1, 10000),在1000并发,每进程10次uniqid的情况下,再没有产生重复。
...全文
3302 31 打赏 收藏 转发到动态 举报
写回复
用AI写文章
31 条回复
切换为时间正序
请发表友善的回复…
发表回复
goosman 2010-11-10
  • 打赏
  • 举报
回复
goosman 2010-11-09
  • 打赏
  • 举报
回复
ihefe 2010-11-09
  • 打赏
  • 举报
回复
参考:http://zh.wikipedia.org/zh/UUID
ihefe 2010-11-09
  • 打赏
  • 举报
回复
用uuid 16字节

类似:550e8400-e29b-41d4-a716-446655440000

ddboy 2010-11-09
  • 打赏
  • 举报
回复
呵呵,我自己写有一个算法,不过效率肯定没原生的高,可能是没有那么大的流量的操作吧, 目前还没见过重复的可能性
原理是一方面取时间精确到 0.0001毫秒, 另一方面加上一个百万级的随机数, 这样其实重复的几率几乎能用0来形容了, 这样得到一串大概是 21位的数字字符串,然后通过64位进行法把它压缩到15个字符以内.
amani11 2010-11-08
  • 打赏
  • 举报
回复

//运行一下,你会发现,两行输出,规则就差前面的1,是因为前缀,true====>1,结果通常也是最后一位不同
echo uniqid(true);
$time = gettimeofday();
echo '<br />';
echo dechex($time['sec']) . dechex($time['usec']);


而php构造uniqid函数返回字符串的核心语句如下
gettimeofday((struct timeval *) &tv, (struct timezone *) NULL);
sec = (int) tv.tv_sec;
usec = (int) (tv.tv_usec % 0x100000);

if (more_entropy) {
spprintf(&uniqid, 0, "%s%08x%05x%.8F", prefix, sec, usec, php_combined_lcg(TSRMLS_C) * 10);
} else {
spprintf(&uniqid, 0, "%s%08x%05x", prefix, sec, usec);
}
goosman 2010-11-08
  • 打赏
  • 举报
回复
回22楼,谢谢。不过我们的应用场景是下面这样,所以加个rand目前是最方便的做法。


<?php
for($i=0; $i<10; $i++)
$ar[rand(1, 100000)]++;
$p = array_filter($ar, 'foo');
echo count($p) . PHP_EOL;
echo array_sum($p) . PHP_EOL;
print_r($p);

function foo($v) {
return $v>1;
}
?>
xuzuning 2010-11-08
  • 打赏
  • 举报
回复
观察如下代码
for($i=0; $i<1000; $i++)
$ar[rand(1, 1000)]++;
$p = array_filter($ar, 'foo');
echo count($p) . PHP_EOL;
echo array_sum($p) . PHP_EOL;
print_r($p);

function foo($v) {
return $v>1;
}

可知,随机数的重复概率是相当高的
床上等您 2010-11-08
  • 打赏
  • 举报
回复
原理就不会了,菜鸟一个。

但手册上是这样写的:
uniqid() returns a prefixed unique identifier based on the current time in microseconds.
基于 mircotime 来构造出来的。
goosman 2010-11-08
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 yangball 的回复:]

rand有可能重复,uniqid也有可能重复,但uniqid是基于mircotime的。

两者加起来,基本不会重复,或者说机率很低微。
[/Quote]

uniqid是基于microtime的?可以讲解一下其中原理吗?谢谢。
床上等您 2010-11-08
  • 打赏
  • 举报
回复
rand有可能重复,uniqid也有可能重复,但uniqid是基于mircotime的。

两者加起来,基本不会重复,或者说机率很低微。
goosman 2010-11-07
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 xuzuning 的回复:]

既然都 function_exists 了,还说什么顺序号不好加?
[/Quote]

子进程里面是调用了已有的业务处理
而uniqid的调用是经过了好多层调用的。。
并且,这些业务处理并不只是在这里的子进程使用,因为我们的应用是web, 但是有独立进程来处理离线业务

顺序号难加的原因:
cli因为有一个while(true)的主控进程,可以加,但是,php 在web中怎么来保持状态?
xuzuning 2010-11-07
  • 打赏
  • 举报
回复
既然都 function_exists 了,还说什么顺序号不好加?
goosman 2010-11-06
  • 打赏
  • 举报
回复
15楼:
谢谢,第一个参数设置为true无意义,这个我没查过
,来项目里这样用,我也就想当然的用了。。。

14楼:
我这边是多进程控制的,所以顺序号是不好加,要加又得共享存储空间来解决。

10楼:
你说的rand 500多次就会重复,我这边是多进程环境,每个进程里面uniqid+rand顶多不超过5次,不知道会不会有同样的问题?

谢谢各位。

helloyou0 2010-11-05
  • 打赏
  • 举报
回复
uniqid(true)

楼上说了,第一个参数是前缀,设成true无意义,

另外,第二个参数设成true,产生23位长串,比不设该参数结果要更唯一
xuzuning 2010-11-05
  • 打赏
  • 举报
回复
出现你说得情况的原因是你的机器太快了
因为 uniqid 产生的是基于系统时间(微秒)的随机数,所以若你的机器太快,就可能出现重码
解决的方法应不是加随机数,而是加定长的顺号
在-云端 2010-11-05
  • 打赏
  • 举报
回复
之前我用的方法是当前时间戳+随即!

无出现重复现象了!本身时间戳是精确到秒的!
kyzy_yy_pm 2010-11-05
  • 打赏
  • 举报
回复
只要超过一定的数rand以后生成的全是重复的没有例外,前一阵子有个通知发个帖子提问过这个问题,如果单纯的靠rand生成大量的数据那么注定会在一段数据之后全变成重复,如果你生成10000个数,那么就前几百个是正常的,后面的全不能用,还小么

感觉楼主尽量将多种生成随机数据的方法连用,时间戳不过,或者楼主将随机数转成asscii,然后unquie+时间戳+随机其他的应该就能将重复的数据降到很低了
在-云端 2010-11-05
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 kyzy_yy_pm 的回复:]

引用 7 楼 heyc1998 的回复:
引用 6 楼 kyzy_yy_pm 的回复:

rand在运算400多次以后就会出现重复

1/400 * 小概率事件   基本上等于不可能!

你自己有机会试一试就知道了,当你想生成1000个数的时候经常会出现后500多个全和前面重复,不知道你有何感想
[/Quote]


我说的前提是小概率事件* 1/10000
kyzy_yy_pm 2010-11-05
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 heyc1998 的回复:]
引用 6 楼 kyzy_yy_pm 的回复:

rand在运算400多次以后就会出现重复

1/400 * 小概率事件   基本上等于不可能!
[/Quote]
你自己有机会试一试就知道了,当你想生成1000个数的时候经常会出现后500多个全和前面重复,不知道你有何感想
加载更多回复(9)

21,887

社区成员

发帖
与我相关
我的任务
社区描述
从PHP安装配置,PHP入门,PHP基础到PHP应用
社区管理员
  • 基础编程社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧