在PDO中使用持久连接有什么缺点

MD *_*med 174 php pdo

在PDO中,可以使用该PDO::ATTR_PERSISTENT属性使连接持久化.根据php手册 -

持久连接不会在脚本末尾关闭,而是在另一个脚本使用相同凭据请求连接时进行高速缓存和重新使用.持久连接缓存允许您避免每次脚本需要与数据库通信时建立新连接的开销,从而产生更快的Web应用程序.

本手册还建议在使用PDO ODBC驱动程序时不要使用持久连接,因为它可能会妨碍ODBC连接池过程.

显然,除了在最后一种情况下,在PDO中使用持久连接似乎没有任何缺点.但是,我想知道使用这种机制是否存在任何其他缺点,即这种机制会导致性能下降或类似的情况.

Cha*_*les 279

请务必阅读下面的答案,其中详细说明了缓解此处所述问题的方法.


与使用持久连接的任何其他PHP数据库接口一样,使用PDO也存在同样的缺点:如果您的脚本在数据库操作过程中意外终止,那么获取剩余连接的下一个请求将在死脚本停止的地方获取.连接在进程管理器级别打开(Apache用于mod_php,当前的FastCGI进程,如果你使用的是FastCGI等),而不是在PHP级别,并且PHP不会告诉父进程让连接在连接时死掉脚本异常终止.

如果死脚本锁定表,那么这些表将保持锁定状态,直到连接中断或获取连接的下一个脚本解锁表本身.

如果死脚本位于事务中间,那么可以阻止大量表,直到死锁计时器启动,即使这样,死锁计时器也可以杀死较新的请求,而不是导致问题的旧请求.

如果死脚本位于事务的中间,则获取该连接的下一个脚本也会获得事务状态.很可能(取决于您的应用程序设计)下一个脚本可能实际上不会尝试提交现有事务,或者在不应该提交时提交,或者在不应该提交时回滚.

这只是冰山一角.通过始终尝试在每个脚本请求上的脏连接之后进行清理,可以在一定程度上减轻这一点,但这可能会很麻烦,具体取决于数据库.除非您已将创建数据库连接标识为脚本中的瓶颈(这意味着您已使用xdebug和/或xhprof完成代码分析),否则不应将持久连接视为任何解决方案.

此外,大多数现代数据库(包括PostgreSQL)都有自己的首选执行连接池的方法,这些方法没有普通的基于PHP的持久连接所带来的直接缺点.


为了澄清一点,我们在工作场所使用持久连接,但不是选择.我们遇到了奇怪的连接行为,从我们的应用服务器到我们的数据库服务器的初始连接正好三秒钟,而它应该花费一小部分时间.我们认为这是一个内核错误.我们放弃了尝试对其进行故障排除,因为它是随机发生的,无法按需复制,我们外包的IT没有具体的能力来追踪它.

无论如何,当仓库里的人正在处理几百个传入的部件,而每个部件需要花费三秒半而不是半秒时,我们必须采取行动才能绑架我们并帮助他们.因此,我们在我们自己开发的ERP/CRM/CMS怪物中翻了几下,并亲身体验了持续连接的所有恐怖.我们花了几周的时间来追踪看似随意发生的所有细微的小问题和奇怪的行为.事实证明,我们的用户努力挤出我们的应用程序的那些每周一次的致命错误正在离开锁定表,废弃交易和其他不幸的不稳定状态.

这个啜泣的故事有一个观点:它以性能的名义打破了我们从未预料到会打破的事情. 这种权衡是不值得的,我们迫不及待地等待着我们可以在没有用户骚乱的情况下切换回正常连接的那一天.

  • 我知道一个大型网站已经使用持久连接近十年了.诀窍是使用DB扩展上方的层,并使用`register_shutdown_function()`记​​住需要清理的内容.如果过程死亡,连接也会死亡.如果没有,则将连接重置为其干净状态(例如,回滚打开的事务).如果失败,则关闭连接,并且在下一个请求到同一进程时将打开一个新连接.没有必要妖魔化持久连接. (30认同)
  • 我们在连接上有5秒的延迟,我们设法将其隔离为DNS + IPv6问题.服务器正在查找v6地址,失败,然后使用IPv4地址. (5认同)
  • 添加了一个轶事,以驱动点回家.请享用! (4认同)
  • 我希望在运行`SELECT orders.*FROM orders LEFT JOIN items USING(item_id)`之前我已经读过这个答案 (2认同)

小智 44

为了回应查尔斯的问题,

来自:http://www.php.net/manual/en/mysqli.quickstart.connections.php -

关于持久连接的常见抱怨是它们的状态在重用之前不会被重置.例如,打开和未完成的事务不会自动回滚.但是,也没有反映在将连接放入池并重新使用之间发生的授权更改.这可能被视为不必要的副作用.相反,持久性名称可以被理解为状态持久的承诺.

mysqli扩展支持持久连接的两种解释:状态持久化,以及重用前的状态重置.默认值已重置.在重用持久连接之前,mysqli扩展隐式调用mysqli_change_user()重置状态.持久连接对用户来说就像刚刚打开一样.以前的用法中没有可见的工件.

mysqli_change_user()功能是一项昂贵的操作.为了获得最佳性能,用户可能希望MYSQLI_NO_CHANGE_USER_ON_PCONNECT在设置了编译标志的情况下重新编译扩展.

用户可以选择安全行为和最佳性能.两者都是有效的优化目标.为了便于使用,安全行为已成为默认值,但最大限度地降低了性能.


Jho*_* H. 13

只有当(相对)长时间连接到数据库时,持久连接才是一个好主意.如今,情况几乎从未如此.持久连接的最大缺点是它限制了您可以浏览站点的用户数量:如果MySQL配置为一次只允许10个并发连接,那么当第11个人尝试浏览您的站点时,它将不适用于他们.

PDO不管理持久性.MySQL驱动程序.它在a)可用且主机/用户/密码/数据库匹配时重用连接.如果有任何更改,则不会重用连接.最好的案例净效应是,您拥有的这些连接将经常启动和停止,因为您在站点上拥有不同的用户并使其持久化并没有任何好处.

了解持久连接的关键是不应该在大多数Web应用程序中使用它们.它们听起来很诱人,但它们很危险,几乎没用.

我确定在这方面还有其他线程,但持久连接是危险的,因为它在请求之间持续存在.例如,如果您在请求期间锁定了一个表,然后无法解锁,那么该表将无限期地保持锁定状态.对于99%的应用程序,持久连接也几乎没用,因为您无法知道在不同请求之间是否使用相同的连接.每个Web线程都有自己的持久连接集,您无法控制哪个线程将处理哪些请求.

PHP的过程mysql库有一个功能,即后续调用mysql_connect将返回相同的链接,而不是打开一个不同的连接(正如人们所预料的那样).这与持久连接无关,并且特定于mysql库.PDO没有表现出这种行为


资源链接:链接

一般情况下,您可以将其用作粗略的"规则集"::

,使用持久连接,如果:

  • 只有少数应用程序/用户访问数据库,即您不会导致200个打开(但可能是空闲)连接,因为在同一主机上共享200个不同的用户.
  • 数据库正在您通过网络访问的另一台服务器上运行

  • (一)应用程序经常访问数据库

,不要使用持久连接,如果:

  • 您的应用程序只需要每小时访问数据库100次.

  • 您有许多Web服务器访问一个数据库服务器

使用持久连接的速度要快得多,尤其是在通过网络访问数据库时.如果数据库在同一台机器上运行,它没有太大的区别,但它仍然快一点.然而 - 正如名称所说 - 连接是持久的,即它保持打开,即使它没有被使用.

问题是,在"默认配置"中,MySQL只允许1000个并行"开放通道".之后,拒绝新连接(您可以调整此设置).因此,如果您有 - 例如 - 20个Web服务器,每个客户端上有100个客户端,并且每个客户端每小时只有一个页面访问权限,那么简单的数学运算将向您显示您需要与数据库建立2000个并行连接.那不行.

Ergo:仅用于有大量请求的应用程序.

  • 行后你的答案是来自http://stackoverflow.com/a/51583/718224的复制粘贴 (3认同)

Gun*_*ein 12

在我的测试中,我有一个超过一秒的连接时间到我的localhost,因此假设我应该使用持久连接.进一步测试显示'localhost'存在问题:

以秒为单位的测试结果(通过php microtime测量):

  • 托管网络:connectDB:0.0038912296295166
  • localhost:connectDB:1.0214691162109(超过一秒:不要使用localhost!)
  • 127.0.0.1:connectDB:0.00097203254699707

有趣的是:以下代码与使用127.0.0.1一样快:

$host = gethostbyname('localhost');
// echo "<p>$host</p>";
$db = new PDO("mysql:host=$host;dbname=" . DATABASE . ';charset=utf8', $username, $password,
    array(PDO::ATTR_EMULATE_PREPARES => false,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Run Code Online (Sandbox Code Playgroud)


Ste*_*hen 6

持久连接应该会提供相当大的性能提升.我不同意你应该"避免"持久性的说法.

听起来上面的抱怨是由使用MyIASM表的人驱动并通过抓取表锁来攻击他们自己的交易版本..当然,你会陷入僵局!使用PDO的beginTransaction()并将表移到InnoDB ..

  • 迟了一年,我意识到,但是为了记录:我的故事来自一个由InnoDB表组成*的完全*的数据库,唯一的例外是少数非常规克隆卡在MyISAM的泥潭中以获得全文索引支持. (2认同)
  • MySQL 5.6为InnoDB表提供了全文支持。 (2认同)