已解决:第二次测试中的值引用似乎是个问题。 而是做了价值($ id,$ t1 ....)'我需要做' VALUES(" $ id"," $ t1" .....),在第二次测试中,这确实引发了错误。只有在手动打印之后我才能看到它。
嘿,我最近一直在使用bindParam,并注意到我的页面的加载时间急剧增加。
所以我花了几个小时来诊断问题,似乎bindParam正在使用 A LOT 更多的处理时间,而不是旧的方式(直接使用查询中的参数)
为了确保结果有效,我做了两次测试。
这是我的测试:
for ($j = 0; $j < 2; $j++) {
unset($t);
$dbh = new PDO('mysql:host=localhost;port=3306', 'root', 'root');
$dbh->query('USE Web_Amicrom_HQ');
$t['start'] = microtime(true);
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$ins = $dbh->prepare('INSERT INTO `test` (id, t1, dt, t2, t3) VALUES(:1, :2, now(), :3, :4)');
$ins->bindParam(':1', $id, PDO::PARAM_INT);
$ins->bindParam(':2', $t1, PDO::PARAM_STR);
$ins->bindParam(':3', $t2, PDO::PARAM_STR);
$ins->bindParam(':4', $t3, PDO::PARAM_STR);
$ins->execute();
}
$t['loop_fact'] = microtime(true);
echo "---- with bindParam ----\n";
$str_result_bench = mini_bench_to($t);
echo $str_result_bench; // string return
echo "\n\n";
}
for ($j = 0; $j < 2; $j++) {
unset($t);
$dbh = new PDO('mysql:host=localhost;port=3306', 'root', 'root');
$dbh->query('USE Web_Amicrom_HQ');
$t['start'] = microtime(true);
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$ins = $dbh->prepare("INSERT INTO `test` (id, t1, dt, t2, t3) VALUES($id, $t1, now(), $t2, $t3)");
$ins->execute();
}
$t['loop_fact'] = microtime(true);
echo "---- with parameter in query ----\n";
$str_result_bench = mini_bench_to($t);
echo $str_result_bench; // string return
echo "\n\n";
}
结果:
---- with bindParam ----
total time : 3136.148ms
---- with bindParam ----
total time : 2645.822ms
---- with parameter in query ----
total time : 41.693ms
---- with parameter in query ----
total time : 52.9752ms
我尝试的事情。
将引号从Double更改为Single - 无差异
没有参数类型的bindParam(例如PDO :: PARAM_INT) - 没有区别
更改了所有参数名称(例如:1到:id等) - 没有区别
这是一个很大的性能差异,特别是对于一些查询。
答案 0 :(得分:2)
在代码的第3行中,您遇到以下错误host=127
,这就是bindParam
基准测试速度太慢的原因
$dbh = new PDO('mysql:host=127.0.0.1;port=3306', 'root', 'root');
修改强>
在您的示例中,您可以避免连接到MySQL服务器4次(2次循环,每次2次),而不是关闭连接一次。此外,不需要对USE
数据库Web_Amicrom_HQ进行额外查询,您可以在连接中传递该信息。
完整代码
$dbh = new PDO('mysql:host=localhost;dbname=Web_Amicrom_HQ', 'root', 'root');
for ($j = 0; $j < 2; $j++) {
$start = microtime(true);
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$ins = $dbh->prepare('INSERT INTO `test` (id, t1, dt, t2, t3) VALUES(:1, :2, now(), :3, :4)');
$ins->bindParam(':1', $id, PDO::PARAM_INT);
$ins->bindParam(':2', $t1, PDO::PARAM_STR);
$ins->bindParam(':3', $t2, PDO::PARAM_STR);
$ins->bindParam(':4', $t3, PDO::PARAM_STR);
$ins->execute();
}
echo "---- with bindParam ----\n";
echo microtime(true) - $start; // time elapsed
echo "\n\n";
}
for ($j = 0; $j < 2; $j++) {
$start = microtime(true);
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$ins = $dbh->prepare("INSERT INTO `test` (id, t1, dt, t2, t3) VALUES($id, $t1, now(), $t2, $t3)");
$ins->execute();
}
echo "---- with bindParam ----\n";
echo microtime(true) - $start; // time elapsed
echo "\n\n";
}
$dbh = null;
答案 1 :(得分:1)
问:为什么PDO :: bindParam这么慢?
答: PDO :: bindParam并不慢。
问题中展示的基准测试不是bindParam性能的有效衡量标准。
第二个测试几乎每个INSERT都会抛出错误。那是因为字符串文字(?)没有用单引号括起来。我原本以为那些会被解释为数字。 (令人困惑的是,我们没有看到string_random(240)
返回的规范。我们没有看到该函数的定义,甚至没有看到返回的任何示例。)
如果返回一个240个字符的字符串,它不代表有效的数字文字...基准测试是比较行的插入数~1500个字符(使用bindParam)与没有插入的行。
我的原始答案(下面)表示对两个测试的修改,以便它们执行相同的功能。这将是bindParam与bindParam的更有效比较。
原始回答
为了提高性能,请在进入循环之前调用prepare
和bindParam
函数一次。 (没有必要多次在同一个SQL语句上调用prepare。)
$sql = 'INSERT INTO `test` (id, t1, dt, t2, t3) VALUES(:1, :2, NOW(), :3, :4)';
$ins = $dbh->prepare($sql);
$id = 0;
$t1 = 0;
$t2 = 0;
$t3 = 0;
$ins->bindParam(':1', $id, PDO::PARAM_INT);
$ins->bindParam(':2', $t1, PDO::PARAM_STR);
$ins->bindParam(':3', $t2, PDO::PARAM_STR);
$ins->bindParam(':4', $t3, PDO::PARAM_STR);
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$ins->execute();
}
如果我们使用bindValue
代替bindParam
,我们需要在执行前立即执行bindValue
。
对于第二个测试(不使用bindParam),与第一个测试不同,代码容易受到SQL注入攻击。为了使第二个测试等同于第一个测试,我们需要确保正确地转义这些值,以便它们可以安全地包含在SQL文本中。
for ($i = 0; $i < 50; $i++) {
$id = rand(1000000, 9999999);
$t1 = string_random(240);
$t2 = string_random(240);
$t3 = string_random(1000);
$sql = "INSERT INTO `test` (id, t1, dt, t2, t3) VALUES ( "
. $dbh->quote( $id ) . ", "
. $dbh->quote( $t1 ) . ", "
. "NOW(), "
. $dbh->quote( $t2 ) . ", "
. $dbh->quote( $t3 ) . ")";
$ins = $dbh->prepare($sql);
$ins->execute();
}
请注意,引用函数不只是在值周围加上引号。它还“逃避”文本中包含的单引号,以及MySQL将解释的其他字符。 (在最初的第二个测试中,我们没有看到值周围的单引号,所以看起来MySQL会在数值上下文中评估它们。我们期望很多这些行在t1中会有“0”, t2和t3列。
我有兴趣看到这两个(修改的)测试的时间比较。