MySQL PDO准备比查询更快?这就是这个简单的测试所显示的

Mar*_*ind 7 php mysql pdo prepared-statement

这是一个简单的测试,我试图快速了解使用MySQL PDO预处理语句与使用直接查询所支付的性能损失.人员表中有2801行.MySQL版本5.5.28和PHP版本5.3.15.香草装置,无论默认参数如何.测试在8GB的iMac上运行.

$pdo = new PDO('mysql:host=localhost;dbname=cwadb_local', 'root', "");
$start = microtime(true);
for ($i = 0; $i < 200; $i++) {
    $pdo->query("select * from person where name_last = 'smith' or true");
}
echo "<p>query: " . (microtime(true) - $start);

$start = microtime(true);
for ($i = 0; $i < 200; $i++) {
    $stmt = $pdo->prepare("select * from person where name_last = :last or true");
    $stmt->execute(array('last' => 'smith'));
}
echo "<p>prepare/execute: " . (microtime(true) - $start);
Run Code Online (Sandbox Code Playgroud)

这是输出:

query: 21.010436058044

prepare/execute: 20.74036192894
Run Code Online (Sandbox Code Playgroud)

这显示没有任何惩罚.可能性:

  • 缓存准备好的语句确实有效.(注意我将prepare函数保留在循环中.)

  • 这是一个虚假的测试,因为它太简单了.

  • 没有理论上的原因,为什么准备/执行应该更慢,并且厌倦了不断的批评,MySQL/PDO/PHP开发人员已经加倍努力使它们更快,以试图让我们所有人闭嘴.

  • 其他?

这里有很多次说使用预处理语句比使用查询更安全,并且在PDO中使用命名参数(Mysqli没有它们),处理参数非常方便.但是,正如经常指出的那样,如果必须在每次执行时都准备好语句,那么就会有性能损失.

那么,有人可以提供一些与我的简单测试相矛盾的测试吗?或者,我们现在是否承认没有理由不使用准备好的陈述?

You*_*nse 10

有一点要提一点.默认情况下,PDO只是模拟预准备语句.
而在仿真模式下,它运行相同的旧查询而不实际准备单个语句:)

所以,首先,

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
Run Code Online (Sandbox Code Playgroud)

准备好真实的准备陈述.

它经常被注意到会有性能损失

还有一件事要提.
可悲的是,世界上几乎没有真正的知识.特别是在Q&A网站的世界.人们倾向于重复他们阅读过的信息,并认为合理.没有进行任何测试来证明甚至没有亲自动手.因此,"经常注意到"不应被视为可靠的来源.

回到这个问题:尽管应该有一些惩罚,但大部分时间都应该是微不足道的.如果是 - 您必须调整系统.

无论如何,在仿真模式下,你既"快速又安全".

更新
好了,在对我的数据运行测试之后,如果你在大型数据集上有3倍的差异,我必须说你的数据库有问题.

对于闪电查询

select title from Board where id = 1
Run Code Online (Sandbox Code Playgroud)

结果是

emulation   on      off
query      0.07    0.130
prepare    0.075   0.145
Run Code Online (Sandbox Code Playgroud)

而对于相当繁琐的查询

select title from Board where id > 1
Run Code Online (Sandbox Code Playgroud)

结果是

emulation   on      off
query      0.96    0.96
prepare    0.96    1.00
Run Code Online (Sandbox Code Playgroud)

因此,正如我们所看到的,在大型数据集上,差异变得不明显.

对于闪电查询存在一些差异,但是,因为它只需要第二个第(000)个派系(对于单个查询) - 我会说这是"无差异"一词的完美示例.

对于query()/ prepare()之间的相同结果 - 我只有一个想法 - PDO对所有查询使用prepare/execute,即使是那些没有绑定的查询.

现在来编码问题.

是的,奇怪的GBK问题确实会影响5.3.3之前版本的PDO.这些版本无法设置正确的编码,并且不可避免地容易受到攻击(在仿真模式下).但是因为5.3.3 PDO支持在DSN中设置编码,现在一切都很好.
对于mysqli,必须使用mysqli_set_charset()相同(不可穿透)的结果来实现此目的.

在我自己的基于mysqli的类中,我使用自己的占位符实现并且根本不使用预处理语句.不是出于性能原因而是为了更好的可靠性


Sam*_*tch 4

我对你的方法有一些疑问:

  1. 除非服务器上绝对没有任何其他东西与脚本同时运行(这种情况不太可能发生),否则您的基本计时器会受到 CPU 调度的影响。为了解决这个问题,编写两个单独的脚本并使用 *nix 的命令运行它们time,即:time php myscript.php
  2. 由于 mySQL 缓存了查询,颠倒脚本的顺序可能会生成相同的结果。
  3. 每一项测试并不能做出诊断。尝试运行每个脚本数百或数千次,然后对结果进行平均以获得更全面的结果。

但是,在非静态查询的情况下,仍然没有理由不使用准备好的语句,除非您喜欢始终严格验证所有输入并且仍然存在 SQL 注入的可能性。

  • 由于这是准备/执行成本的时序测试,因此不将准备放入循环中将使测试无效。(这是一个计时测试,而不是如何编写程序的秘诀!) (2认同)