PHP PDO:重新准备语句如何影响性能

rav*_*ren 9 php mysql pdo prepared-statement

我正在编写一个半简单的数据库包装类,并希望有一个自动运行的提取方法:它应该只在第一次准备每个不同的语句,并且只是在连续调用时绑定并执行查询.

我想主要的问题是:如何重新准备相同的MySql语句工作,PDO会神奇地识别语句(所以我没有)并停止操作?

如果没有,我打算通过为每个不同的查询生成唯一键来实现这一目的,并将准备好的语句保存在数据库对象的私有数组中 - 在其唯一键下.我打算以下列方式之一获取数组密钥(我都不喜欢).按优先顺序排列:

  • 让程序员在调用方法时传递一个额外的,总是相同的参数 - basename(__FILE__, ".php") . __LINE__这个方法是这样的(只有在循环中调用我们的方法时这个方法才有效 - 大多数情况下需要这个功能)
  • 让程序员传递一个完全随机的字符串(很可能是事先生成的)作为额外的参数
  • 使用传递的查询本身来生成密钥 - 获取查询的哈希或类似的东西
  • 通过调用实现与第一个子弹(上面)相同 debug_backtrace

有没有类似的经历?虽然我正在为之工作的系统确实值得关注优化(它本周非常大并且在不断增长),但也许我一点也不担心,做我正在做的事情没有性能上的好处?

sym*_*ean 6

MySQL(与大多数DBMS一样)将缓存预准备语句的执行计划,因此如果用户A为以下内容创建计划:

SELECT * FROM some_table WHERE a_col=:v1 AND b_col=:v2
Run Code Online (Sandbox Code Playgroud)

(其中v1和v2是绑定变量)然后发送要由DBMS插值的值,然后用户B发送相同的查询(但具有不同的插值值),DBMS不必重新生成计划.即它是DBMS找到匹配计划 - 而不是PDO.

但是,这意味着数据库上的每个操作都需要至少2次往返(第一次显示查询,第二次显示绑定变量),而不是使用文字值进行查询的单次往返,这会引入额外的网络成本.解除引用(和维护)查询/计划缓存还涉及一小部分成本.

关键问题是这个成本是否大于首先产生计划的成本.

虽然(根据我的经验)使用Oracle编写的语句肯定会有性能优势,但我不相信MySQL的情况也是如此 - 但是,很多东西将取决于数据库的结构和复杂性.查询(或更具体地说,优化程序可以找到多少个用于解析查询的选项).

尝试自己测量它(提示:您可能希望将慢查询阈值设置为0并编写一些代码以将文字值转换回写入日志的查询的匿名表示).


Ins*_*lah 1

好吧,由于我一直在抨击为缓存设置查询键的方法,除了简单地使用查询字符串本身之外,我还做了一个简单的基准测试。以下比较了使用纯查询字符串与首先创建 md5 哈希值的情况:

$ php -v
$ PHP 5.3.0-3 with Suhosin-Patch (cli) (built: Aug 26 2009 08:01:52)
$ ...
$ php benchmark.php
$ PHP hashing: 0.19465494155884 [microtime]
$ MD5 hashing: 0.57781004905701 [microtime]
$ 799994
Run Code Online (Sandbox Code Playgroud)

代码:

<?php
error_reporting(E_ALL);

$queries = array("SELECT",
                 "INSERT",
                 "UPDATE",
                 "DELETE",
                 );
$query_length = 256;
$num_queries  = 256;
$iter = 10000;

for ($i = 0; $i < $num_queries; $i++) {
    $q = implode('',
           array_map("chr",
             array_map("rand",
                       array_fill(0, $query_length, ord("a")),
                       array_fill(0, $query_length, ord("z")))));
    $queries[] = $q;
}

echo count($queries), "\n";

$cache = array();
$side_effect1 = 0;
$t = microtime(true);
for ($i = 0; $i < $iter; $i++) {
    foreach ($queries as $q) {
        if (!isset($cache[$q])) {
            $cache[$q] = $q;
        }
        else {
            $side_effect1++;
        }
    }
}
echo microtime(true) - $t, "\n";

$cache = array();
$side_effect2 = 0;
$t = microtime(true);
for ($i = 0; $i < $iter; $i++) {
    foreach ($queries as $q) {
        $md5 = md5($q);
        if (!isset($cache[$md5])) {
            $cache[$md5] = $q;
        }
        else {
            $side_effect2++;
        }
    }
}
echo microtime(true) - $t, "\n";

echo $side_effect1 + $side_effect2, "\n";
Run Code Online (Sandbox Code Playgroud)