为什么模拟预处理语句不与数据库服务器通信?

时间:2018-01-28 17:29:42

标签: php mysql mysqli pdo

我正在学习PHP PDO,并对模拟的预处理语句感到困惑。什么是模拟准备语句和本地准备语句。

参考链接说:

  

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

参考链接:Resource

2 个答案:

答案 0 :(得分:2)

声明“不与服务器通信”可能不清楚。可以更清楚地说“当你调用prepare()时不与服务器通信。”

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

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

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

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

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

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

    但我想不出任何常用的SQL数据库品牌缺乏对查询参数的支持。

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

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

  3. PDO 0.1 alpha可以追溯到2004年。那时,如果你不得不为每个查询与数据库服务器通信两次,有些人可能会担心网络开销过高。因此,为了减少网络旅行,可能已经创建了使用模拟预处理语句。

    但如果您的应用对性能敏感,则应在快速网络(1Gbps或10Gbps)上运行应用和数据库服务器。任何认真对待性能的现代数据中心都应该在内部使用这些快速网络。

答案 1 :(得分:1)

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

由于PDO必须与大量数据库一起工作,而不仅仅是像MySQL这样的常见情况,它需要涵盖不支持预处理语句并且必须模拟它们的情况。

当他们被模拟时,它无法知道查询是否有效,它只是希望一旦最终查询被编写并传递到服务器就能解决。换句话说,尽管查询的行为就好像 已经准备好了,但转义正确完成后,实际上并没有使用预准备语句执行。

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

如果您对最低级别的工作方式感到好奇,那么MySQL binary protocol documentation就是一个很好的例子,其中有一节介绍如何从机械上讲,实现预备语句系统。