【问题标题】:mysqli prepared statements buffered data vs unbuffered data Performance?mysqli准备语句缓冲数据与非缓冲数据性能?
【发布时间】:2014-10-12 06:44:25
【问题描述】:

使用 mysqli 预处理语句在 mysql 中使用 缓冲数据与非缓冲数据 提高或降低性能百分比。

1.缓冲数据。 例如

$query = "SELECT Name, CountryCode FROM City ORDER by ID DESC LIMIT 150,5";

        if ($stmt = mysqli_prepare($link, $query)) {

        /* execute statement */
        mysqli_stmt_execute($stmt);

        /* bind result variables */
        mysqli_stmt_bind_result($stmt, $row['Name'], $row['CountryCode']);

        /* store result */
        mysqli_stmt_store_result($stmt);

        /* fetch values */
        while (mysqli_stmt_fetch($stmt)) {
            echo $row['Name'].'-'. $row['CountryCode'];
        }

        /* free result */
        mysqli_stmt_free_result($stmt);

        /* close statement */
        mysqli_stmt_close($stmt);
    }

/* close connection */
mysqli_close($link);    

2。无缓冲数据。 例如

$query = "SELECT Name, CountryCode FROM City ORDER by ID DESC LIMIT 150,5";

        if ($stmt = mysqli_prepare($link, $query)) {

        /* execute statement */
        mysqli_stmt_execute($stmt);

        /* bind result variables */
        mysqli_stmt_bind_result($stmt, $row['Name'], $row['CountryCode']);

        /* fetch values */
        while (mysqli_stmt_fetch($stmt)) {
            echo $row['Name'].'-'. $row['CountryCode']);
        }

        /* close statement */
        mysqli_stmt_close($stmt);
    }

/* close connection */
mysqli_close($link);    

第一个例子使用

mysqli_stmt_store_result($stmt);

将结果数据存储在缓冲区中以供以后获取

而另一个示例直接将数据提取到客户端而不存储在缓冲区中。

您必须为每个查询调用 mysqli_stmt_store_result() 成功生成结果集(SELECT、SHOW、DESCRIBE、EXPLAIN), 当且仅当您想缓冲由 客户端,以便随后的 mysqli_stmt_fetch() 调用返回 缓冲数据。

参考:http://php.net/manual/en/mysqli-stmt.store-result.php

所以我的问题是,如果使用mysqli_stmt_store_result,性能会提高多少?

【问题讨论】:

  • 无法访问您的数据和特定设置,只有您可以准确回答该问题。您可以设置一个计时器并在循环中运行查询,您会看到它有多大的不同。
  • @Pier-LucGendreau 谢谢,一定会做到的。
  • 如果你这样做了,如果你发布你的方法和发现作为答案也会很有帮助。
  • 为什么要在这个用例中缓冲数据?实际上,使用mysqli_store_result() 的主要用例是您想要任意访问结果集中的行(例如,使用mysqli_data_seek())。对于您的特定用例,您是否尝试过运行一些测试以查看性能差异?确定应用程序和数据性能的最佳方法是对其进行测试。

标签: php mysql mysqli prepared-statement


【解决方案1】:

虽然我从来不需要存储结果,但我仍然觉得这个问题很有趣,并且想知道同样的事情。我创建了一个包含垃圾数据的数据库,并对它进行了一些测试,以查看使用无缓冲和缓冲结果的时间差异。我已经包含了我的测试和发现,以防它对您(或其他任何人)有所帮助。

数据库设置

数据库由一个包含四个不同字段的表组成。这是架构:

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 之间的随机数,最后两个字段 NameDescription 包含 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; 

?>

【讨论】:

  • 感谢详细解答,
  • @AMB 欢迎您!我也从中学到了一些东西。也感谢您的赏金!
【解决方案2】:

我认为 90% 的时间你不会看到任何不同,因为:

1- 如果您有少量数据,一切都会足够快,不会有太大的不同。

2- 如果您有大量数据,这些数据将被放入缓冲区客户端,只有在您多次使用相同的结果集时才能节省时间。

很遗憾,我无法给您一个明确的答案,因为只有您知道查询将产生的数据量以及是否需要不止一次。

【讨论】:

    猜你喜欢
    • 2010-11-29
    • 1970-01-01
    • 2012-01-14
    • 2014-08-02
    • 2010-11-08
    • 1970-01-01
    • 2014-01-30
    • 1970-01-01
    相关资源
    最近更新 更多