mysqli_fetch_assoc()性能PHP5.4 vs PHP7.0

Zmr*_*zka 19 php mysql performance mysqli multidimensional-array

我有大量的MySQL查询(1.8M行,25列),我需要从中创建二维数组(基于主键的内存表).

代码按预期工作,但$ table创建在PHP7.0中需要很长时间.

PHP7.0表现如此糟糕的原因是什么?我的主要兴趣在于mysqli.

感谢您的任何见解 - 如果我可以修复性能,PHP7会为我节省大量内存.

mysqli代码片段

$start = microtime(true);

$vysledek = cluster::query("SELECT * FROM `table` WHERE 1");
$query_time = (microtime(true) - $start);
$start_fetch = microtime(true);
while($zaznam = mysqli_fetch_assoc ( $vysledek )){
  $fetch_time+= (microtime(true) - $start_fetch);
  $start_assign = microtime(true);
  $table[$zaznam['prikey']] = $zaznam;
  $assign_time+= (microtime(true) - $start_assign);
  $start_fetch = microtime(true);
}
$total_time+= (microtime(true) - $start);
echo round($assign_time, 2).' seconds to set the array values\n';
echo round($query_time, 2).' seconds to execute the query\n';
echo round($fetch_time, 2).' seconds to fetch data\n';
echo round($total_time, 2).' seconds to execute whole script\n';
echo "Peak Memory Usage:".round(memory_get_peak_usage(true)/(1024 * 1024), 2)." MB\n";
Run Code Online (Sandbox Code Playgroud)

mysqli的结果

Deb 7 PHP 5.4 mysqlnd 5.0.10
1.8秒设置数组值
8.37秒执行查询
13.49秒获取数据
24.42秒执行整个脚本
峰值内存使用情况:8426.75 MB

Deb 8 PHP 5.6 mysqlnd 5.0.11-dev
1.7秒设置数组值
8.58秒执行查询
12.55秒获取数据
23.6秒执行整个脚本
Peak Memory Usage:8426.75 MB

Deb 8 PHP 7.0 mysqlnd 5.0.12-dev
0.73秒设置数组值
8.63秒执行查询
126.71秒获取数据
136.46秒执行整个脚本

峰值内存使用情况:7394.27 MB

Deb 8 PHP 7.0 mysqlnd 5.0.12-dev扩展基准测试

我已经扩展了部分提取的基准测试,以报告每100k行,结果如下:

在1.87s中获取100000行在5.24s中
获取300000行在10.97s中
获取500000行在19.17s中
获取700000行在29.96s中
获取900000行在43.03s中
获取1100000行在58.48s中
获取1300000行在76.47s中
获取1500000
行获取的行在96.73s中的1700000行在107.78s中
获得了1800000

DEB8 PHP7.1.0-dev libclient 5.5.50

1.56秒设置数组值
8.38秒执行查询
456.52秒获取数据
467.68秒执行整个脚本

峰值内存使用率:8916 MB

DEB8 PHP7.1.0-dev libclient 5.5.50扩展基准测试

在2.72s中获取100000行在15.7s中
获取300000行在38.7s中
获取500000行在71.69s中
获取700000行在114.8s中
获取900000行在168.18s中
获取1100000行在231.69中
获取1300000行在305.36中
获取1500000
行获取的行389.05s中的1700000行在434.71s中
获得了1800000

DEB8 PHP7.1.0-dev mysqlnd 5.0.12-dev

1.51秒设置数组值
9.16秒执行查询
261.72秒获取数据
273.61秒执行整个脚本

峰值内存使用率:8984.27 MB

DEB8 PHP7.1.0-dev mysqlnd 5.0.12-dev扩展基准测试

在3.3s中获取100000行在13.63s中
获取300000行在29.02s中
获取500000行在49.21s中
获取700000行在74.56s中
获取900000行在104.97s中
获取1100000行在140.03s中
获取1300000行在180.42s中
获取1500000
行获取的行在225.72s中的1700000行在250.01s中
获得了1800000

PDO代码段

$start = microtime(true);
$sql = "SELECT * FROM `table` WHERE 1";
$vysledek = $dbh->query($sql, PDO::FETCH_ASSOC);
$query_time = (microtime(true) - $start);
$start_fetch = microtime(true);
foreach($vysledek as $zaznam){
  $fetch_time+= (microtime(true) - $start_fetch);
  $start_assign = microtime(true);
  $table[$zaznam['prikey']] = $zaznam;
  $assign_time+= (microtime(true) - $start_assign);
  $start_fetch = microtime(true);
}
$total_time+= (microtime(true) - $start);
echo round($assign_time, 2).' seconds to set the array values\n';
echo round($query_time, 2).' seconds to execute the query\n';
echo round($fetch_time, 2).' seconds to fetch data\n';
echo round($total_time, 2).' seconds to execute whole script\n';
echo "Peak Memory Usage:".round(memory_get_peak_usage(true)/(1024 * 1024), 2)." MB\n";
Run Code Online (Sandbox Code Playgroud)

PDO结果

Deb 7 PHP 5.4 mysqlnd 5.0.10
1.85秒设置数组值
12.51秒执行查询
16.75秒获取数据
31.82秒执行整个脚本
峰值内存使用情况:11417.5 MB

Deb 8 PHP 5.6 mysqlnd 5.0.11-dev
1.75秒设置数组值
12.16秒执行查询
15.72秒获取数据
30.39秒执行整个脚本
峰值内存使用情况:11417.75 MB

Deb 8 PHP 7.0 mysqlnd 5.0.12-dev
0.71秒设置数组值
35.93秒执行查询
114.16秒获取数据
151.19秒执行整个脚本

峰值内存使用情况:6620.29 MB

基线比较代码

 $start_query = microtime(true);
 exec("mysql --user=foo --host=1.2.3.4 --password=bar -e'SELECT * FROM `profile`.`table`' > /tmp/out.csv");
 $query_time = (microtime(true) - $start_query);
 echo round($query_time, 2).' seconds to execute the query \n';
Run Code Online (Sandbox Code Playgroud)

所有系统的执行时间类似于19秒+ -1秒变化.

基于上述观察,我会说PHP 5.X是合理的,因为执行的工作比仅仅转储到文件要多一些.

  • 所有3台服务器都在同一台主机上(源和两台测试服务器)
  • 重复时测试是一致的
  • 内存中已经存在类似的变量,我需要将其作为比较删除进行测试,与问题无关
  • CPU整个时间都是100%
  • 两台服务器都将32G RAM和swappiness设置为1,目标是将其作为内存操作执行
  • 测试服务器是专用的,没有别的东西在运行
  • php.ini在主要版本之间发生了变化,但与mysqli/PDO相关的所有选项似乎都是相同的
  • 在将PHP7重新安装回来后,Deb8机器被降级为PHP5.6并且问题消失了

  • 在php.net上报告了一个错误 - ID 72736,因为我相信已证明问题出在PHP中,而不是在系统或任何其他配置中

编辑1:添加了PDO比较

编辑2:添加基准测试标记,编辑PDO结果,因为存在基准测试错误

编辑3:原始问题中的主要清理,重建代码snipets以更好地指示错误

编辑4:关于降级和升级PHP的补充点

编辑5:为DEB8 PHP7.0添加了扩展基准测试

编辑6:包含php7配置

编辑7:PHP 7.1 dev的性能测量与两个库 - 使用来自bishop的配置编译,删除了我的php-config

编辑8:添加了与CLI命令,次要清理的比较

hak*_*kre 4

供交叉参考:随着2016 年 12 月 1 日PHP 7.1的发布,这个问题应该得到解决(在 PHP 7.1 中)。

PHP 7.0:即使在票证中写着 PHP-7.0 已被修补,我还没有在最近的更改日志(2016 年 11 月 10 日的 7.0.13,自补丁合并日期以来)中看到这是当前的一部分PHP 7.0.x 版本。也许在下一个版本中。

该错误已在上游跟踪(感谢 OP 的报告):Bug #72736 - 使用 mysqli / PDO 获取大型数据集时性能缓慢(bugs.php.net;2016 年 8 月)。