首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PDO MySQL:使用PDO::ATTR_EMULATE_PREPARES还是不使用?

问PDO MySQL:使用PDO::ATTR_EMULATE_PREPARES还是不使用?
EN

Stack Overflow用户
提问于 2012-04-11 20:37:33
回答 8查看 71.8K关注 0票数 132

这就是我到目前为止读到的关于PDO::ATTR_EMULATE_PREPARES的文章

  1. PDO's prepare emulation is better for performance since MySQL's native prepare bypasses the query cache.
  2. MySQL's native prepare is better for security (preventing SQL Injection).
  3. MySQL's native prepare is better for error reporting.

我不知道这些说法有多真实。在选择MySQL接口时,我最关心的是防止SQL注入。第二个问题是表现。

我的应用程序目前使用过程MySQLi (没有准备好的语句),并且相当多地使用查询缓存。它很少在单个请求中重用已准备好的语句。为了获得已准备语句的命名参数和安全性,我开始迁移到PDO。

我用的是MySQL 5.1.61和PHP 5.3.2

我是否应该让PDO::ATTR_EMULATE_PREPARES启用?是否有一种方法既具有查询缓存的性能,又具有准备好的语句的安全性?

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2012-05-04 19:48:39

要回答你的担忧:

  1. MySQL >= 5.1.17 (或用于PREPARE和EXECUTE语句的>= 5.1.21 ) can use prepared statements in the query cache。因此,您的MySQL+PHP版本可以在查询缓存中使用准备好的语句。但是,请仔细注意MySQL文档中缓存查询结果的注意事项。有许多类型的查询不能被缓存,或者即使它们被缓存,也是无用的。根据我的经验,查询缓存通常不是一个很大的胜利。查询和架构需要特殊的构造才能最大限度地利用缓存。通常,从长远来看,应用程序级缓存是必要的。
  2. 本机准备对安全性没有任何影响。伪准备语句仍将转义查询参数值,它将只在带有字符串的PDO库中完成,而不是在MySQL服务器上使用二进制协议完成。换句话说,无论您的EMULATE_PREPARES设置如何,相同的PDO代码都同样容易受到注入攻击的攻击(或不受攻击)。唯一的区别是参数替换发生在哪里--使用EMULATE_PREPARES,它发生在PDO库中;没有EMULATE_PREPARES,它发生在MySQL EMULATE_PREPARES上,您可能会在准备时而不是在执行时得到语法错误;对于EMULATE_PREPARES,只有在执行时才会得到语法错误,因为在执行之前,PDO没有提供给MySQL的查询。请注意,这会影响您将要编写的代码!尤其是如果您使用的是PDO::ERRMODE_EXCEPTION!

另一项考虑是:

  • -- prepare()有固定的成本(使用本机准备语句),因此带有本机准备语句的prepare();execute()可能比使用模拟准备语句发出简单文本查询要慢一些。在许多数据库系统中,prepare()的查询计划也是缓存的,并且可以与多个连接共享,但我认为MySQL不会这样做。因此,如果不将准备好的语句对象用于多个查询,则总体执行速度可能会更慢。

作为最后的推荐,我认为对于较早版本的MySQL+PHP,您应该模仿准备好的语句,但是对于最近的版本,您应该关闭模拟。

在编写了一些使用PDO的应用程序之后,我制作了一个PDO连接函数,它具有我认为最好的设置。您可能应该使用类似的东西或调整到您喜欢的设置:

代码语言:javascript
复制
/**
 * Return PDO handle for a MySQL connection using supplied settings
 *
 * Tries to do the right thing with different php and mysql versions.
 *
 * @param array $settings with keys: host, port, unix_socket, dbname, charset, user, pass. Some may be omitted or NULL.
 * @return PDO
 * @author Francis Avila
 */
function connect_PDO($settings)
{
    $emulate_prepares_below_version = '5.1.17';

    $dsndefaults = array_fill_keys(array('host', 'port', 'unix_socket', 'dbname', 'charset'), null);
    $dsnarr = array_intersect_key($settings, $dsndefaults);
    $dsnarr += $dsndefaults;

    // connection options I like
    $options = array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    );

    // connection charset handling for old php versions
    if ($dsnarr['charset'] and version_compare(PHP_VERSION, '5.3.6', '<')) {
        $options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$dsnarr['charset'];
    }
    $dsnpairs = array();
    foreach ($dsnarr as $k => $v) {
        if ($v===null) continue;
        $dsnpairs[] = "{$k}={$v}";
    }

    $dsn = 'mysql:'.implode(';', $dsnpairs);
    $dbh = new PDO($dsn, $settings['user'], $settings['pass'], $options);

    // Set prepared statement emulation depending on server version
    $serverversion = $dbh->getAttribute(PDO::ATTR_SERVER_VERSION);
    $emulate_prepares = (version_compare($serverversion, $emulate_prepares_below_version, '<'));
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate_prepares);

    return $dbh;
}
票数 120
EN

Stack Overflow用户

发布于 2019-11-13 03:47:12

我很惊讶没有人提到关闭仿真的最大原因之一。在进行仿真之后,PDO将所有整数和浮点数作为字符串返回。当您关闭模拟时,MySQL中的整数和浮点数在PHP中变成整数和浮点数。

有关更多信息,请参见该问题的公认答案:PHP + PDO + MySQL: how do I return integer and numeric columns from MySQL as integers and numerics in PHP?。

票数 23
EN

Stack Overflow用户

发布于 2016-09-29 19:36:22

当您的PDO::ATTR_EMULATE_PREPARES pdo_mysql没有针对mysqlnd编译时,请注意禁用mysqlnd(打开本机准备)。

由于旧libmysql与某些函数不完全兼容,因此可能会导致奇怪的错误,例如:

  1. 在绑定64位整数时会丢失最重要的位(在64位机器上将0x12345678AB裁剪到0x345678AB )
  2. 无法进行简单的查询,例如LOCK TABLES (它抛出SQLSTATE[HY000]: General error: 2030 This command is not supported in the prepared statement protocol yet异常),
  3. 需要在下一个查询之前从结果或关闭游标中获取所有行(使用mysqlnd或模拟准备)h 214G 215,这样就会自动为您工作,并且不会与mysql服务器同步。

当迁移到使用libmysql作为pdo_mysql模块的其他服务器时,我在简单的项目中发现了这些bug。也许还有更多的虫子,我不知道。我还在新鲜的64位debian上进行了测试,所有列出的bug在我apt-get install php5-mysql时都会出现,当我apt-get install php5-mysqlnd时会消失。

当PDO::ATTR_EMULATE_PREPARES设置为true时(默认情况下)-这些bug无论如何都不会发生,因为PDO在这种模式下根本不使用准备好的语句。因此,如果您使用基于pdo_mysql的libmysql ("mysqlnd“子字符串不出现在phpinfo中pdo_mysql部分的"Client版本”字段中),则不应该关闭PDO::ATTR_EMULATE_PREPARES。

票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10113562

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档