我应该在PHP PERFORMANCE-WISE中使用MySQL的预处理语句吗?

时间:2010-02-06 19:31:46

标签: php mysql performance pdo prepared-statement

我理解MySQL中预准备语句的安全性好处。这里不需要涉及这个主题。我想知道他们的表现方面。

现在,我知道当使用预准备语句的查询在单个PHP脚本中执行两次时,它更快,因为查询只被解析一次,每次查询一次。客户端进行一次准备,然后使用二进制协议发送数据两次。二进制协议速度更快,而且您不必再次进行解析。

但是,我只想在一个PHP脚本中执行一次查询的情况呢?看起来使用准备好的声明会更糟糕,因为你要两次前往服务器,一次准备,一次发送数据。只需要解析一次的好处就丢失了,你第二次旅行就受到了惩罚。如果数据的二进制格式不够小,那么使用准备好的语句会丢失,对吗?

但是,我已经阅读了一些关于PHP的mysqli或PDO库做什么的相互矛盾的报道?它们中的任何一个是否跨脚本执行缓存预准备语句?服务器是否必须在后续页面加载时再次解析预准备语句?如果答案是否定的,那么不必在第二个页面加载上解析该语句,那么看起来准备好的语句会更好,即使你每页面加载只执行一次查询。

请考虑MySQL版本之间是否有任何相关变化。您可以放心地假设我使用的是PHP 5.2

编辑:为了说清楚,我想要专门针对MySQL和PHP的答案,指定MySQL版本,如果这是不同的,只考虑性能,而不是易用性或安全性。

更新:我接受了我所做的答案,因为后续评论有一些好主意。我仍然有点失望,似乎没有人能够回答我所提出的实际问题的症结。我猜有时答案真的是“这取决于。”

5 个答案:

答案 0 :(得分:12)

历史

这是我的第一个Stackoverflow答案。自那以后发生了很多变化,特别是对mysql API的弃用和删除。即使你仍然使用php 5.6,也不应该使用mysql_ * api。现在PDO或mysqli是唯一可供选择的选项。由于很多原因,PDO更好。

是否在页面加载中缓存了预准备语句?

  

我已经阅读了一些关于PHP的mysqli或PDO的相互矛盾的报道   图书馆呢?它们中的任何一个都会缓存预准备的语句   脚本执行?

在页面加载之间不会使用相同的预准备语句。它必须每次都准备好。如果压缩每个大毫秒都很重要,那么存储过程可能是一个好主意(假设您有一个复杂的查询)。

对于大型插入(数千行)可以通过将数据转储到文本文件并使用LOAD DATA IN FILE加载来获得更大的提升。它比一系列插件快得多。

原始答案

事情的真相是,有时mysqli更快,而有时mysql api更快。但差异真的很小。如果你看一下网上的任何性能测试,差异实际上只有10到20毫秒。提高性能的最佳方法是优化表格设计。

许多测试“证明”旧的api更快更方便地忘记了为了最大的安全性,应该为查询中使用的每个变量调用mysql_real_escape_string()。

当且仅当查询中使用的所有表上的数据保持不变时,服务器才会缓存查询。

等待具有实际数字的其他更新

答案 1 :(得分:7)

“使用准备好的陈述绝不是一个坏主意”

不正确。如果您不知道自己在做什么,可以轻松填写​​预准备语句缓存并超过max_prepared_stmt_count,在关闭使用预准备语句的连接之前,无法使应用程序无效。

预备语句专门针对业务逻辑中的紧密内部循环而设计,您将一遍又一遍地调用相同的基本查询,这是一个参数化查询的好例子,例如:

SELECT name, address, phone from tbl WHERE id = ?

并且每次通话都有不同的ID。这样,值得额外往返数据库进行准备,因为你可能要调用数百或数千次,只需更改参数即可。但是你应该从缓存中删除准备好的语句,或者在你的无状态脚本(php,perl,jsp,ruby等)的末尾关闭连接。
如果您没有删除准备好的语句并且您正在使用连接池,则必然会随着时间的推移填满缓存并出现一个令人讨厌的错误“无法创建超过max_prepared_stmt_count语句”。我是根据经验说的,所以想想你是否真的需要准备好的陈述,因为你明确知道你将在一个紧密的循环中反复重复相同的参数化查询。
如果没有,你可能正在寻找的是让mysql使用它的基本查询缓存,这是一种与预备语句列表不同的机制,其行为就像我理解的真正的LRU缓存一样。

答案 2 :(得分:1)

并非适用于所有情况。有些时候准备好的语句比手动构建的查询执行更慢,即使它重复运行

例如,当我需要在一个页面加载中插入数百行时,我发现:

$pdo -> query("INSERT INTO table (field1, field2, ...) VALUES (".$pdo -> quote($value1).", ".$pdo -> quote($value2).", ...), (row2), (row3), ..., (row100), ...");

快于:

$stmt = $pdo -> prepare("INSERT INTO table (field1, field2, ...) VALUES (:value1, :value2, ...)");
foreach (/* some hundred times */) {
  $stmt -> execute(array('value1' => $value1, 'value2' => $value2, ...));
}

更新

4年前我写了这个答案,从那以后我就学到了新的东西。

对于反复运行数百次的预准备语句,您必须将所有执行包装在一个事务中。

但是我从来没有测试哪个是更快,未准备好的单批INSERT,包含1000行或1000个执行的已准备好的INSERT包装在一个事务中。

答案 3 :(得分:0)

使用预准备语句绝不是一个坏主意。我最近没有在mySQL中使用过它们,但我在SQLSERVER上大量使用它们 - 它们运行得很好。

PDO库可能不会执行任何缓存,但数据库将用于准备好的语句。

另外,如您所述,它们非常适合安全性。将代码推出代码也非常好 - 你不会在整个应用程序中使用SQL。

答案 4 :(得分:0)

你究竟是什么意思“在第二次页面加载时解析”?

每次执行相同脚本的PHP进程/线程/访问者都不会阻止MySQL重新解析查询以准备语句,因为它在每个连接的基础上有效,所以它对于在同一个连接中重复查询非常有效脚本。

现在,我不能争论准备+执行与简单查询,性能明智(没有重复查询);

但是,为了保存mysql在每个脚本执行中解析复杂查询,你应该使用存储过程;查询只是CALL items(date,search_etc,id);而不是SELECT i.id FROM products as p JOIN item_prodct as t ON... JOIN items as i... ON... WHERE ... p.date ... i.search_etc ...

您可以将存储过程与预准备语句一起使用。