【问题标题】:Why emulated prepared statements does not communicate with database server?为什么模拟的准备好的语句不与数据库服务器通信?
【发布时间】:2018-07-07 10:37:48
【问题描述】:

我正在学习 PHP PDO,但对模拟的准备好的语句感到困惑。什么是模拟预编译语句和原生预编译语句。

参考链接说:

模拟的预处理语句不与数据库通信 服务器所以 PDO::prepare() 不会检查语句。

参考链接:Resource

【问题讨论】:

    标签: php mysql mysqli pdo


    【解决方案1】:

    “不与服务器通信”的说法可能不清楚。说“调用prepare()时不与服务器通信”会更清楚。

    通常在准备和执行序列期间,当您调用 prepare() 时,客户端会将 SQL 作为字符串发送到服务器。服务器解析 SQL 语法,如果 SQL 语法有错误,或引用不存在的表,或其他错误,则返回错误。如果查询有效,则将查询保存在服务器上。如果您的 SQL 字符串中有 ? 之类的参数占位符,服务器会记录这些占位符。

    随后,您调用 execute(),然后传递任何值来替换占位符。这可能不涉及字符串替换,因为查询在被解析和分析后不再存储为字符串。查询的每个元素都存储为一个对象,包括常量值和参数占位符。

    使用模拟准备,PDO 在prepare() 期间跳过将SQL 字符串发送到服务器。它只是将 SQL 字符串保存在客户端的内存中的 PDO 代码中。这也跳过了检查 SQL 查询的错误,因为 PDO 客户端不包括 SQL 解析器或不知道存在哪些表等。

    当您执行()保存为模拟准备查询的查询时,PDO 对保存的 SQL 字符串进行字符串替换,然后将最终的 SQL 字符串提交给服​​务器。这是服务器第一次看到该查询,因此它会解析语法并在当时优化查询。

    模拟准备至少有三个可能的原因:

    1. 允许 PDO 支持某些品牌的 SQL 数据库的 prepare()/execute(),这些数据库本身不支持这种用法,或者不支持查询参数。

      但是我想不出哪个常用品牌的SQL数据库不支持查询参数。

    2. 允许 PDO 支持任何品牌的 SQL 数据库的位置参数 (?) 和命名参数 (:user_id)。 MySQL 原生只支持位置参数。 Oracle 仅支持命名参数。 SQLite 支持这两种样式。

      但 PDO 确实找到了一种在 prepare() 之前将一种类型的参数映射到另一种类型的方法,因此您可以在 MySQL 中使用命名参数,即使在使用非模拟准备时也是如此。 PDO 可以在通过 prepare() 发送查询字符串之前重写查询字符串,将命名参数转换为位置参数,并记住哪个位置对应哪个名称,因此当您调用 execute() 时,一定会以正确的顺序发送值。

    3. PDO 0.1 alpha 可以追溯到 2004 年。当时,有些人可能会担心,如果每次查询都必须与数据库服务器通信两次,网络开销太大。因此,为了减少网络旅行,可能已经创建了使用模拟的准备好的语句。

      但是,如果您的应用对性能如此敏感,您应该在 1Gbps 或 10Gbps 的快速网络上彼此靠近地运行应用和数据库服务器。任何注重性能的现代数据中心都应该在内部使用这些快速网络。

    【讨论】:

    • 又是什么意思 PDO::prepare() 不检查语句 ?
    • @StressdBrain,PDO 不会将 SQL 字符串发送到服务器,并且不会对您的 SQL 语法正确进行任何客户端验证。我将在上面编辑我的答案以包含它。
    【解决方案2】:

    PDO 以两种不同的模式运行,具体取决于您配置连接的方式。您可以使用PDO::ATTR_EMULATE_PREPARES 选项更改它的操作方式,使用setAttribute 方法如果您的数据库驱动程序支持该功能。

    由于 PDO 必须处理大量数据库,而不仅仅是 MySQL 等常见情况,它需要涵盖不支持预准备语句的情况,并且必须模拟它们。

    当它们被模拟时,它无法知道查询是否有效,它只希望在最终查询完成并交付给服务器后能够解决问题。换句话说,尽管查询表现得好像它准备好了,但转义已正确完成,但实际上并没有使用准备好的语句执行。

    如果它们是本地的,您可以获得一个准备好的语句句柄,它(通常)将验证查询至少具有有效的语法。

    如果您对它在最低级别的工作方式感到好奇,一个很好的例子是MySQL binary protocol documentation,其中有一个部分介绍了如何实现预准备语句系统。

    【讨论】:

    • 又是什么意思 PDO::prepare() 不检查语句 ?
    • 在模拟模式下,它只是假定语句是有效的。在本机模式下,它会将其转发到服务器进行验证。如果您的语法有效,则差异只是技术性问题,但本机准备需要有效的连接,因此它们可能会在不同的地方出错。从异常中拯救时需要考虑的事项。
    猜你喜欢
    • 1970-01-01
    • 2017-06-12
    • 1970-01-01
    • 2011-07-09
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    相关资源
    最近更新 更多