代码之家  ›  专栏  ›  技术社区  ›  Mike Sherov

我应该在PHP性能方面使用针对MySQL的准备语句吗?

  •  21
  • Mike Sherov  · 技术社区  · 16 年前

    我理解MySQL中预处理语句的安全好处。这里不需要讨论这个话题。我想知道它们的性能方面。

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

    但是,如果我只想在一个PHP脚本中执行一次查询,情况会怎样?使用prepared语句似乎更糟糕,因为您要两次访问服务器,一次准备,一次发送数据。只需解析一次的好处就消失了,第二次则会受到惩罚。如果二进制格式的数据不够小,那么使用准备好的语句会丢失数据,对吗?

    然而,我读过一些关于PHP的mysqli或PDO库做什么的相互矛盾的报告?它们中的任何一个是否在脚本执行期间缓存准备好的语句?服务器是否必须在后续页面加载中再次解析准备好的语句?如果答案是否定的,即不必在第二次页面加载时解析语句,那么准备好的语句似乎更好,即使每个页面加载只执行一次查询。

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

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

    更新:我接受了我的答案,因为后续评论有一些好主意。我仍然有点失望,因为似乎没有人能够肯定地回答我提出的实际问题的症结所在。我想有时候答案真的是“视情况而定”

    5 回复  |  直到 16 年前
        1
  •  12
  •   e4c5    9 年前

    历史

    这是我的第一个答案。此后发生了很多变化,特别是mysql API的弃用和删除。即使您仍然使用PHP5.6,也不应该使用mysql_*api。现在,PDO或mysqli是唯一的选择。PDO有很多优点。

    准备好的语句是否跨页面加载缓存?

    我读过一些关于PHP的mysqli或PDO的相互矛盾的报告 图书馆怎么办?它们中的任何一个是否都会跨存储准备好的语句 脚本执行?

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

    对于大型插入(数千行),通过将数据转储到文本文件并在文件中加载数据,可能会获得更大的提升。它比一系列插入要快得多。

    原始答案

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

    许多“证明”旧api更快的测试都很方便地忘记了,为了获得最大的安全性,应该为查询中使用的每个变量调用mysql\u real\u escape\u string()。

    等待另一个实际数字的更新

        2
  •  8
  •   instead    10 年前

    “使用事先准备好的语句从来都不是一个好主意”

    max_prepared_stmt_count 如果你不知道自己在做什么,那么在使用准备好的语句的连接关闭之前,你的应用程序将变得无用。

    预处理语句是专门为业务逻辑中的紧密内部循环而设计的,在业务逻辑中,您将反复调用相同的基本查询,一个很好的示例是参数化查询,例如:

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

    每次通话你都有不同的身份证。这样,为prepare额外往返db是值得的,因为您可能会调用它数百次或数千次,只需更改参数。但是您需要从缓存中删除准备好的语句,或者在无状态脚本(php、perl、jsp、ruby等)的末尾关闭连接。
    如果您不删除准备好的语句,并且正在使用连接池,那么随着时间的推移,您肯定会填满缓存,并出现严重错误“不能创建超过max_prepared_stmt_count的语句”。我是根据经验说的,所以考虑一下您是否真的需要准备好的语句,因为您明确地知道,您将在一个紧密的循环中一遍又一遍地重复相同的参数化查询。
    如果不是,那么您可能希望让mysql使用它的基本查询缓存,这是一种不同于预处理语句列表的机制,据我所知,它的行为类似于真正的LRU缓存。

        3
  •  1
  •   datasn.io    8 年前

    更慢的 而不是手动构造的查询 即使它反复运行 .

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

    $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年前我写了这个答案,从那以后我学到了新的东西。

    为了使准备好的语句能够快速地重复运行数百次,您必须将所有执行都打包在 一笔交易 .

    然而,我从未测试过哪一种更快,非准备的单批插入,1000行或1000次准备好的插入被包装在一个事务中执行。

        4
  •  0
  •   mr-sk    16 年前

    使用事先准备好的语句从来都不是一个好主意。我最近没有在mySQL中使用它们,但我在SQLSERVER中大量使用了它们——它们工作得很好。

    PDO库可能不进行任何缓存,但数据库将为准备好的语句进行缓存。

    另外,当你报道时,它们非常安全。将这种逻辑从代码中推出来也很好——您不会在整个应用程序中使用SQL。

        5
  •  0
  •   Luxvero    16 年前

    “在第二页加载时解析”到底是什么意思?

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

    现在,我不能争论prepare+execute与简单查询、性能方面(没有重复查询);

    但是,为了避免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 ...