前置条件:
所有测试生成的都写入一个新文件,如果时同一个文件名,那么每次执行脚本前,需要把该日志文件删掉,确保每次执行时日志文件都是重新创建的。
每次执行都是往日志文件中使用多进程写入90000行日志。每种方式分成四种对照组测试:
30*3000 加锁(即30个进程每个进程写入3000行,总共90000行,写入时需对日志文件上独占锁)。
30*3000 不加锁(即30个进程每个进程写入3000行,总共90000行,写入时日志文件不上锁)。
90*1000 加锁(即90个进程每个进程写入1000行,总共90000行,写入时需对日志文件上独占锁)。
90*1000 不加锁(即90个进程每个进程写入1000行,总共90000行,写入时日志文件不上锁)。
方式一:
使用file_put_contents() 函数写入文件。为了避免内容覆盖,须使用FILE_APPEND模式写入。
加锁:(n=3000 | n=1000)
for($i=0;$i<n,$i++){
$msg = "test text";
file_put_contents($log, $msg, FILE_APPEND|LOCK_EX);
}
不加锁:(n=3000 | n=1000)
for($i=0;$i<n,$i++){
$msg = "test text";
file_put_contents($log, $msg, FILE_APPEND);
}
执行情况如下表:
序号 |
进程数 |
每个进程写入行数 |
是否加锁 |
第一次执行平均耗时(s) |
第二次执行平均耗时(s) |
第三次执行平均耗时(s) |
1-1 |
30 |
3000 |
Y |
2.831 |
2.815 |
2.861 |
1-2 |
30 |
3000 |
N |
2.826 |
2.855 |
2.751 |
1-3 |
90 |
1000 |
Y |
2.407 |
2.396 |
2.278 |
1-4 |
90 |
1000 |
N |
1.779 |
2.052 |
2.01 |
方式二:
加锁:(n=3000 | n=1000)
$handle = fopen($log,’a’);
flock($handle,LOCK_EX);
for($i=0;$i<n,$i++){
$msg = "test text";
fwrite($handle,$msg);
}
flock($handle,LOCK_UN);
fclose($handle);
不加锁:(n=3000 | n=1000)
$handle = fopen($log,’a’);
for($i=0;$i<n,$i++){
$msg = "test text";
fwrite($handle,$msg);
}
fclose($handle);
执行情况如下表:
序号 |
进程数 |
写入行数/每个进程 |
是否加锁 |
第一次执行平均耗时(s) |
第二次执行平均耗时(s) |
第三次执行平均耗时(s) |
2-1 |
30 |
3000 |
Y |
0.66 |
0.659 |
0.658 |
2-2 |
30 |
3000 |
N |
1.272 |
1.17 |
1.161 |
2-3 |
90 |
1000 |
Y |
0.83 |
0.855 |
0.836 |
2-4 |
90 |
1000 |
N |
0.952 |
1.097 |
0.947 |
以方式一跟方式二的表格为参照,同一种方式,上不上锁,性能相差不是很大,从效率上讲,方式二要比方式一高效。
最根本的原因是file_put_contents()函数每次执行相当于执行了 fopen(),fwrite(),fclose()三个函数,所以单次执行耗时会比较长。
如果把方式二做个调整,比如把fopen()和fclose都放进for循环里,那么方式二跟方式一基本没太大差别。
for($i=0;$i<n,$i++){
$handle = fopen($log,’a’);
$msg = "test text";
fwrite($handle,$msg);
fclose($handle);
}
不上锁的情况,日志写进去时无序的,各个进程之间穿插着写入日志。
上锁的情况,日志相对有序,基本是一个进程写完才轮到另一个进程。但是进程之间也是无序的。所以,同一个进程写的日志才是有序的。
<?php set_time_limit(30); $log = ‘/data/tmp/a.log‘; for($i = 0;$i<30;$i++){ pcntl_signal(SIGCHLD, SIG_IGN); $fid = pcntl_fork(); if($fid === 0){ try { $start = microtime(true); $handle = fopen($log,‘a‘); flock($handle,LOCK_EX); for($j=0;$j<3000;$j++){ $start_time = microtime(true); //TODO 其他业务逻辑 //打点记录并行任务执行状况 $fid = posix_getpid(); $ffid = posix_getppid(); $date = date(‘YmdHis‘); $end_time = microtime(true); $usetime = round($end_time-$start_time,2); $msg = PHP_EOL."序号:{$i}:{$j}; 时间:{$date}; 当前进程ID:{$fid}; 父进程ID:{$ffid}; 任务开始:{$start_time}; 任务结束:{$end_time}; 耗时:{$usetime}"; //file_put_contents($log,$msg,FILE_APPEND|LOCK_EX); fwrite($handle,$msg); } flock($handle,LOCK_UN); fclose($handle); unset($handle); $end = microtime(true); $s = round($end-$start,3); echo PHP_EOL.$s.‘,‘; //echo "进程:{$i},开始:{$start},结束:{$end},耗时:{$s}".PHP_EOL; }finally{ if(function_exists("posix_kill")){ posix_kill(getmypid(),SIGTERM); }else{ system(‘kill -9 ‘.getmypid()); } } } } echo ‘over‘.PHP_EOL;