虽然我从来不需要存储结果,但我仍然觉得这个问题很有趣,并且想知道同样的事情。我创建了一个包含垃圾数据的数据库,并对它进行了一些测试,以查看使用无缓冲和缓冲结果的时间差异。我已经包含了我的测试和发现,以防它对您(或其他任何人)有所帮助。
数据库设置
数据库由一个包含四个不同字段的表组成。这是架构:
ID | int(5) | primary_key, auto_increment
Name | tinytext | not_null
CountryCode | int(3) | not_null
Description | tinytext | not_null
该表有超过 10,000 行全部包含垃圾数据。 ID 填充了它的自增值,CountryCode 是一个介于 11 和 999 之间的随机数,最后两个字段 Name 和 Description 包含 md5 数字数据的哈希值。
测试
总共完成了六种不同的测试。前 5 个测试是比较未缓冲和缓冲结果之间的处理时间。测试的总体目标是查看从表格开头到中间的不同大小结果集的基准。最终测试只是随机访问与顺序访问缓冲结果的基准。未缓冲的结果不能通过mysqli_stmt_data_seek() 任意获得,因此很难进行比较并且被认为是公平的。
基准时间使用microtime() 计算。刻度在 MySQLi 语句准备好之前开始,并在语句关闭后立即结束。
以下是 6 项测试的细分:
-
测试 1:比较从表的开头选择的 100 行结果集的无缓冲/缓冲处理时间。
-
测试 2: 比较从表中间中选择的 100 行结果集的无缓冲/缓冲处理时间。
-
测试 3: 比较从表的开头选择的 1000 行结果集的非缓冲/缓冲处理时间。
-
测试 4: 比较从表中间中选择的 1000 行结果集的无缓冲/缓冲处理时间。
-
测试 5: 比较从表的开头选择并重复 3 次的 5000 行结果集的无缓冲/缓冲处理时间。
-
测试 6: 10000 行结果集的处理时间基准,随机访问,重复 3 次。
结果
PHP 为上述测试生成了以下输出。
Test 1
Took 0.002000093460083 seconds to process unbuffered result of 100 rows from the beginning of the table
Took 0.0019998550415039 seconds to process buffered result of 100 rows from the beginning of the table
Test 2
Took 0.012001037597656 seconds to process unbuffered result of 100 rows from the middle of the table
Took 0.011001110076904 seconds to process buffered result of 100 rows from the middle of the table
Test 3
Took 0.013001918792725 seconds to process unbuffered result of 1000 rows from the beginning of the table
Took 0.012001037597656 seconds to process buffered result of 1000 rows from the beginning of the table
Test 4
Took 0.023001909255981 seconds to process unbuffered result of 1000 rows from the middle of the table
Took 0.020002126693726 seconds to process buffered result of 1000 rows from the middle of the table
Test 5
Took 0.19601988792419 seconds to process unbuffered result of 5000 rows sequentially, three times
Took 0.085008144378662 seconds to process buffered result of 5000 rows sequentially, three times
Test 6
Took 4.2634270191193 seconds to process buffered result of 10000 rows randomly, three times
结论
测试 1-4 表明,性能提升在很大程度上可以忽略不计。即使在处理大量行或从表中的各种偏移量中获取记录时,缓冲结果的收益也很小。偏移位置会增加一些开销(大约百分之一秒来推进 5000 行)。
也就是说,缓冲仍然占有一席之地。在测试 5 中,包含数千行的结果集被多次迭代,使用缓冲结果有明显的好处。该测试的缓冲和非缓冲版本都有效地处理了 15,000 行。但是由于缓冲版本不必再次检索结果,它能够在不到非缓冲版本一半的时间内完成工作。
正如其他人在这个问题中已经指出的那样,缓冲在必须任意/随机访问行时非常有用。测试六简单地显示了一个缓冲的 10,000 行集合可以随机访问多长时间,然后再重复两次。测试六有效地提取了完全乱序的 30,000 行。
当然是代码
这是我用来创建此测试的代码。这都是程序化的,所以看起来不是最漂亮的,但如果我发现自己用它创建了一个类或修改代码来清理它,我一定会在这里更新它!
<?php
//tell PHP not to mind how long it is running
set_time_limit(0);
//control output for test results
ob_start();
//array to hold time values from the tests
$times = array();
//Connect to the database
$connection = mysqli_connect("localhost", "root", "", "blah");
/***********************************************************************
* TEST 1: Small result set of 100 rows from the beginning of the table
**********************************************************************/
$times['Test 1'] = array();
//UNBUFFERED VERSION
$benchmarkStart = microtime(true);
$stmt = mysqli_prepare($connection, "SELECT * FROM City LIMIT 100");
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $id, $name, $code, $desc);
while (mysqli_stmt_fetch($stmt))
{
printf("%d, %s, %d, %s \n", $id, $name, $code, $desc);
}
mysqli_stmt_close($stmt);
$benchmarkEnd = microtime(true);
$time = $benchmarkEnd - $benchmarkStart;
$times['Test 1'][] = "Took $time seconds to process unbuffered result of 100 rows from the beginning of the table";
//BUFFERED VERSION
$benchmarkStart = microtime(true);
$stmt = mysqli_prepare($connection, "SELECT * FROM City LIMIT 100");
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $id, $name, $code, $desc);
mysqli_stmt_store_result($stmt);
while (mysqli_stmt_fetch($stmt))
{
printf("%d, %s, %d, %s \n", $id, $name, $code, $desc);
}
mysqli_stmt_free_result($stmt);
mysqli_stmt_close($stmt);
$benchmarkEnd = microtime(true);
$time = $benchmarkEnd - $benchmarkStart;
$times['Test 1'][] = "Took $time seconds to process buffered result of 100 rows from the beginning of the table";
echo "\n ** END TEST 1** \n\n";
/*******************************************************************
* TEST 2: Small result set of 100 rows from the middle of the table
******************************************************************/
$times['Test 2'] = array();
//UNBUFFERED VERSION
$benchmarkStart = microtime(true);
$stmt = mysqli_prepare($connection, "SELECT * FROM City LIMIT 5000, 100");
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $id, $name, $code, $desc);
while (mysqli_stmt_fetch($stmt))
{
printf("%d, %s, %d, %s \n", $id, $name, $code, $desc);
}
mysqli_stmt_close($stmt);
$benchmarkEnd = microtime(true);
$time = $benchmarkEnd - $benchmarkStart;
$times['Test 2'][] = "Took $time seconds to process unbuffered result of 100 rows from the middle of the table";
//BUFFERED VERSION
$benchmarkStart = microtime(true);
$stmt = mysqli_prepare($connection, "SELECT * FROM City LIMIT 5000, 100");
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $id, $name, $code, $desc);
mysqli_stmt_store_result($stmt);
while (mysqli_stmt_fetch($stmt))
{
printf("%d, %s, %d, %s \n", $id, $name, $code, $desc);
}
mysqli_stmt_free_result($stmt);
mysqli_stmt_close($stmt);
$benchmarkEnd = microtime(true);
$time = $benchmarkEnd - $benchmarkStart;
$times['Test 2'][] = "Took $time seconds to process buffered result of 100 rows from the middle of the table";
echo "\n ** END TEST 2** \n\n";
/***********************************************************************
* TEST 3: Large result set of 1000 rows from the beginning of the table
**********************************************************************/
$times['Test 3'] = array();
//UNBUFFERED VERSION
$benchmarkStart = microtime(true);
$stmt = mysqli_prepare($connection, "SELECT * FROM City LIMIT 1000");
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $id, $name, $code, $desc);
while (mysqli_stmt_fetch($stmt))
{
printf("%d, %s, %d, %s \n", $id, $name, $code, $desc);
}
mysqli_stmt_close($stmt);
$benchmarkEnd = microtime(true);
$time = $benchmarkEnd - $benchmarkStart;
$times['Test 3'][] = "Took $time seconds to process unbuffered result of 1000 rows from the beginning of the table";
//BUFFERED VERSION
$benchmarkStart = microtime(true);
$stmt = mysqli_prepare($connection, "SELECT * FROM City LIMIT 1000");
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $id, $name, $code, $desc);
mysqli_stmt_store_result($stmt);
while (mysqli_stmt_fetch($stmt))
{
printf("%d, %s, %d, %s \n", $id, $name, $code, $desc);
}
mysqli_stmt_free_result($stmt);
mysqli_stmt_close($stmt);
$benchmarkEnd = microtime(true);
$time = $benchmarkEnd - $benchmarkStart;
$times['Test 3'][] = "Took $time seconds to process buffered result of 1000 rows from the beginning of the table";
echo "\n ** END TEST 3** \n\n";
/********************************************************************
* TEST 4: Large result set of 1000 rows from the middle of the table
*******************************************************************/
$times['Test 4'] = array();
//UNBUFFERED VERSION
$benchmarkStart = microtime(true);
$stmt = mysqli_prepare($connection, "SELECT * FROM City LIMIT 5000, 1000");
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $id, $name, $code, $desc);
while (mysqli_stmt_fetch($stmt))
{
printf("%d, %s, %d, %s \n", $id, $name, $code, $desc);
}
mysqli_stmt_close($stmt);
$benchmarkEnd = microtime(true);
$time = $benchmarkEnd - $benchmarkStart;
$times['Test 4'][] = "Took $time seconds to process unbuffered result of 1000 rows from the middle of the table";
//BUFFERED VERSION
$benchmarkStart = microtime(true);
$stmt = mysqli_prepare($connection, "SELECT * FROM City LIMIT 5000, 1000");
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $id, $name, $code, $desc);
mysqli_stmt_store_result($stmt);
while (mysqli_stmt_fetch($stmt))
{
printf("%d, %s, %d, %s \n", $id, $name, $code, $desc);
}
mysqli_stmt_free_result($stmt);
mysqli_stmt_close($stmt);
$benchmarkEnd = microtime(true);
$time = $benchmarkEnd - $benchmarkStart;
$times['Test 4'][] = "Took $time seconds to process buffered result of 1000 rows from the middle of the table";
echo "\n ** END TEST 4** \n\n";
/******************************************************************************
* TEST 5: Work with larger result set, 5000 rows, multiple times, sequentially
*****************************************************************************/
$times['Test 5'] = array();
//UNBUFFERED VERSION
$benchmarkStart = microtime(true);
$stmt = mysqli_prepare($connection, "SELECT * FROM City LIMIT 5000");
for ($i = 0; $i < 3; $i++)
{
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $id, $name, $code, $desc);
while (mysqli_stmt_fetch($stmt))
{
printf("%d, %s, %d, %s \n", $id, $name, $code, $desc);
}
}
mysqli_stmt_close($stmt);
$benchmarkEnd = microtime(true);
$time = $benchmarkEnd - $benchmarkStart;
$times['Test 5'][] = "Took $time seconds to process unbuffered result of 5000 rows sequentially, three times";
//BUFFERED VERSION
$benchmarkStart = microtime(true);
$stmt = mysqli_prepare($connection, "SELECT * FROM City LIMIT 5000");
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $id, $name, $code, $desc);
mysqli_stmt_store_result($stmt);
for ($i = 0; $i < 3; $i++)
{
mysqli_stmt_data_seek($stmt, 0);
while (mysqli_stmt_fetch($stmt))
{
printf("%d, %s, %d, %s \n", $id, $name, $code, $desc);
}
}
mysqli_stmt_free_result($stmt);
mysqli_stmt_close($stmt);
$benchmarkEnd = microtime(true);
$time = $benchmarkEnd - $benchmarkStart;
$times['Test 5'][] = "Took $time seconds to process buffered result of 5000 rows sequentially, three times";
echo "\n ** END TEST 5** \n\n";
/***************************************************************************
* TEST 6: Work with larger result set, 10000 rows, multiple times, randomly
**************************************************************************/
$times['Test 6'] = array();
//UNBUFFERED VERSION
//Can't test unbuffered result sets randomly as mysqli_stmt_data_seek
//only works on buffered results.
//BUFFERED VERSION
$benchmarkStart = microtime(true);
$stmt = mysqli_prepare($connection, "SELECT * FROM City LIMIT 10000");
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $id, $name, $code, $desc);
mysqli_stmt_store_result($stmt);
for ($i = 0; $i < 3; $i++)
{
$rows = range(0, (mysqli_stmt_num_rows($stmt) - 1));
shuffle($rows);
for ($j = 0; $j < 10000; $j++)
{
mysqli_stmt_fetch($stmt);
printf("%d, %s, %d, %s \n", $id, $name, $code, $desc);
$row = $rows[0];
mysqli_stmt_data_seek($stmt, $row);
array_shift($rows);
}
}
mysqli_stmt_free_result($stmt);
mysqli_stmt_close($stmt);
$benchmarkEnd = microtime(true);
$time = $benchmarkEnd - $benchmarkStart;
$times['Test 6'][] = "Took $time seconds to process buffered result of 10000 rows randomly, three times";
echo "\n ** END TEST 6** \n\n";
/*******************
* Print the results
******************/
$output = ob_get_clean();
foreach ($times as $tests => $results)
{
echo $tests . "\n";
foreach ($results as $result)
{
echo $result . "\n";
}
echo "\n\n";
}
//Dumps all of those rows that have been getting printed out to the browser.
//This kicked out a little north of 64,000 lines in my browser.
echo $output;
?>