开发者社区> 问答> 正文

PHP利用共享内存构建计数器完成自增数值id生成适用于网页游戏携带区服号的数值id生成:报错

我们之前的id生成是直接用的php的uniqid
存在的问题有:
1. 高并发下容易重复:当高并发适用uniqid来产生唯一id的时候,我的测试数据是:1000并发,每并发产生1000次,实际输出63万uid(可能是标准输出原因未全部输出),5次重复
2. mysql中使用字符串类型的key存在性能问题
使用这里创建的ud_uniqid带来的改变:
优点1. 高并发重复几率降低:我的测试数据是:1000并发,每并发产生1000次,实际输出93万,无重复
优点2. 使用过程中,可以产生唯一的递增数值id,作为mysql主键有性能优势
缺点1. 性能比uniqid略低,耗时约为uniqid的1.5倍(不过在10e-5 -- 10e-4级别的时间差,1.5倍应该可以忽略)
缺点2. windows不能使用(windows不支持usleep)
缺点3. 由于使用了共享内存,所以需要在服务器开启的时候读取上一次的最大值,服务器关闭的时候写入当前的最大值
下面直接上代码了, 代码里面注释很清楚的:

<?php
/**
* functions: 产生64位唯一自增随机数,用于游戏唯一数值id生成
* 1. 数值分布:9位区号 3位服号 32位id号 10位随机码 10位随机码
* 2. 可提供范围:512区,每区8个游戏服务器,提供42亿id
* 3. 重复性测试:100并发,每个并发进程产生1000次随机数,无重复
* 4. 性能测试:与系统自带uniqid函数相比,耗时为其1.5倍
* author: selfimpr
* blog: http://blog.csdn.net/lgg201
* mail: lgg860911@yahoo.com.cn
*/
/**
* 产生子进程
* $func_name: 子进程处理的过程函数
* 可接不定参数,为子进程过程函数需要的参数
*/
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;
}
}
/**
* 计数生成器
* 采用共享内存生成
* $key: 每个独立的$key标记为一个计数器
* $length: 分配内存大小
*/
function counter($key = 0x1, $length = 256) {
$segment_id = shmop_open($key, 'c', 0777, $length);
$now = intval(shmop_read($segment_id, 0, shmop_size($segment_id))) + 1;
shmop_write($segment_id, (string)$now, 0);
shmop_close($segment_id);
return $now;
}
/**
* 自定义唯一id生成器
* $group_id: 区号
* $server_id: 服号
*/
function ud_uniqid($group_id = 1, $server_id = 1) {
$rand_key1 = rand(0, 1023);
usleep(rand(0, 16));
$rand_key2 = rand(0, 1023);
$id = counter();
return ($group_id << 55) | ($server_id << 52) | ($id << 20) | ($rand_key1 <=0) {
if($f[$l] == $f[$l + 1]) echo $f[$l + 1]."\n";
$l --;
}
}
/**
* 生成uid并输出到标准输出
* $times: 产生次数
*/
function check_repeat_out($times) {
while($i ++ < $times) {
echo ud_uniqid()."\n";
}
}
/**
* 测试重复性(并发生成uid并输出到标准输出)
* $concurrent: 并发数
* $times: 每个并发中产生uid次数
*/
function test_repeat($concurrent, $times) {
while($i ++ < $concurrent) {
new_child(check_repeat_out, $times);
}
}
/**
* 与系统自带uniqid性能对比测试
* $times: 运行多少次进行时间比对
*/
function test_performence($times) {
$time = 0;
while($i ++ < $times) {
$start = microtime(true);
ud_uniqid();
$end = microtime(true);
$time += $end - $start;
}
echo "ud_uniqid($times): $time.\n";
$time = 0;
$i = 0;
while($i ++ repeat.check.dat
* 3. 关闭test_repeat();方法(注释)
* 4. 打开check_repeat('repeat.check.dat'); (测试时请注意文件路径),运行:php ud_uniqid.php,如果不产生输出,则标明无重复
* 性能测试:
* 打开test_performence(); CLI模式运行即可
*/
//test_performence(10000);
//test_repeat(100, 1000);
//check_repeat('repeat.check.dat');
?>

展开
收起
kun坤 2020-06-07 13:32:54 556 0
1 条回答
写回答
取消 提交回答
  • 问一下: selfimpr 是你吗?

    2020-06-07 13:33:02
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
阿里云栖开发者沙龙PHP技术专场-直面PHP微服务架构挑战-高驰涛 立即下载
PHP安全开发:从白帽角度做安全 立即下载
PHP 2017.北京 全球开发者大会——高可用的PHP 立即下载