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

在 PDO 中,可以使用 PDO::ATTR_PERSISTENT属性使连接持久化

持久连接不会在脚本结束时关闭,但是 当另一个脚本使用 持久连接缓存允许您 时建立新连接的开销 脚本需要与数据库对话,导致一个更快的网络 申请。

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

所以显然在 PDO 中使用持久连接似乎没有缺点,除了最后一种情况。然而,我想知道使用这种机制是否还有其他缺点,例如,这种机制会导致性能下降或类似的情况。

75951 次浏览

在我看来,拥有一个持久的连接会消耗更多的系统资源。也许是一个微不足道的数量,但仍然..。

请务必阅读 下面的答案,它详细说明了缓解这里概述的问题的方法。


使用 PDO 与其他任何执行持久连接的 PHP 数据库接口存在相同的缺点: 如果脚本在数据库操作中意外终止,获得剩余连接的下一个请求将在死脚本停止的地方继续执行。连接保持在进程管理器级别(Apache for mod _ PHP,当前 FastCGI 进程,如果您使用 FastCGI 等) ,而不是在 PHP 级别,并且 PHP 不会告诉父进程让连接在脚本异常终止时终止。

如果死脚本锁定了表,那么这些表将保持锁定状态,直到连接终止,或者下一个获取连接的脚本解锁表本身。

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

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

这只是冰山一角。通过总是尝试清理每个脚本请求上的脏连接,可以在一定程度上减轻这种情况,但这取决于数据库。除非您已经在脚本中将创建数据库连接标识为 唯一的瓶颈(这意味着您已经使用 Xdebug和/或 Xhprof完成了代码分析) ,否则您应该考虑将持久连接作为解决任何问题的方案。

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


为了澄清一个问题,我们在工作场所使用持久性联系,但并非出于自愿。我们遇到了 真奇怪连接行为,从我们的应用服务器到数据库服务器的初始连接花费了 没错三秒钟,而它应该只花费了几分之一秒的时间。我们认为是内核错误。我们放弃了对它进行故障排除的尝试,因为它是随机发生的,不能按需复制,而且我们的外包 IT 没有具体的能力来跟踪它。

不管怎样,当仓库里的人在处理几百个进来的零件时每个零件都只需要三秒半而不是半秒我们必须在他们绑架我们之前采取行动让我们帮助他们。因此,我们在自己研发的 ERP/CRM/CMS 怪物中翻转了几个位,亲身体验了持久连接的所有恐怖。我们花了 几个星期追踪所有微妙的小问题和奇怪的行为,似乎随机发生。事实证明,那些每周一次的致命错误,我们的用户勤奋地挤出我们的应用程序离开锁定表,放弃事务和其他不幸的摇摆不定的状态。

这个悲伤的故事有一个观点: 它打破了我们从未想过会打破的东西,这一切都是以表演的名义。这种交易不值得,我们热切地期待着有一天我们可以切换回正常的连接,而不会引起用户的骚乱。

持久的连接应该给出一个可观的性能提升。我不同意你应该“避免”持久性的说法。

听起来,上面的抱怨似乎是由使用 MyIASM 表的人通过抓取表锁来侵入他们自己版本的事务所引起的。.你当然会陷入僵局!使用 PDO 的 startTransaction ()并将表移动到 InnoDB。.

作为对查尔斯上述问题的回应,

发信人: http://www.php.net/manual/en/mysqli.quickstart.connections.php-

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

Mysqli 扩展支持持久连接的两种解释: 状态持久化和重用前的状态重置。默认设置为重置。在重用持久连接之前,mysqli 扩展隐式调用 mysqli_change_user()来重置状态。持久连接在用户看来就像刚刚打开过一样。以前使用的工件都不可见。

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

用户可以在安全行为和最佳性能之间做出选择。两者都是有效的优化目标。为了便于使用,以牺牲最大性能为代价将安全行为设置为默认行为。

使用持久连接的原因显然是减少了相当昂贵的连接数量,尽管与其他数据库相比,使用 MySQL 的连接速度要快得多。

第一个问题就是持久的联系。

如果你每秒创建1000个连接,你通常不会保证它长时间保持打开状态,但是操作系统可以。基于 TCP/IP 协议的端口不能立即回收,还必须在“ FIN”阶段等待一段时间,然后才可能被回收。

第二个问题... 使用大量的 MySQL 服务器连接。

许多人只是没有意识到你可以增加 * max _ connect * 变量并获得超过100个 MySQL 并发连接。其他人被老 Linux 的问题打败了,因为不能用 MySQL 传输超过1024个连接。

现在讨论为什么在 mysqli 扩展中禁用了持久连接。尽管您可能会误用持久连接并获得较差的性能,但这并不是主要原因。真正的原因是——你可以得到更多的问题与它。

在 MySQL 3.22/3.23的情况下,持久连接被放入 PHP 中,当时 MySQL 并没有那么困难,这意味着您可以很容易地回收连接,没有任何问题。然而,在后来的版本中,问题的数量出现了——是否应该回收带有未提交事务的连接,这会给您带来麻烦。如果使用自定义字符集配置重复使用连接,那么您将再次处于危险之中,而且每个会话变量都可能被转换。

使用持久连接的一个问题是它的伸缩性不是很好。对于那些有5000个连接的人,你需要5000个持久的连接。除去对持久性的要求,你可能有能力为10000人提供类似数量的联系,因为当他们不在一起的时候,他们可以分享个人联系。

只有在连接到数据库需要(相对)较长时间的情况下,持久连接才是一个好主意。现在几乎没有这种情况。持久连接的最大缺点是它限制了你可以浏览你的网站的用户数量: 如果 MySQL 被配置为一次只允许10个并发连接,那么当第11个人试图浏览你的网站时,它将不能为他们工作。

PDO 不管理持久性。MySQL 驱动程序可以。当连接可用且主机/用户/密码/数据库匹配时,它重用连接。如果有任何更改,那么它将不会重用连接。最好的情况是,这些连接会经常被启动和停止,因为站点上有不同的用户,让他们持久化没有任何好处。

要理解持久连接的关键是你不应该在大多数 web 应用程序中使用它们。它们听起来很诱人,但它们很危险,而且几乎毫无用处。

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

PHP 的过程性 mysql 库有一个特性,即随后对 mysql _ connect 的调用将返回相同的链接,而不是打开一个不同的连接(正如人们可能期望的那样)。这与持久连接无关,而是特定于 mysql 库。PDO 没有表现出这种行为


资源连结: 链接

一般来说,你可以用这个作为一个粗略的“规则集”: :

YES ,使用持久连接,如果:

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

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

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

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

  • 你有很多很多的网络服务器访问一个数据库服务器

使用持久连接相当快,特别是在通过网络访问数据库时。如果数据库在同一台机器上运行,那么结果并没有太大的不同,但是速度还是要快一些。然而,正如名称所说,连接是持久的,即它保持打开状态,即使它没有被使用。

问题是,在“默认配置”中,MySQL 只允许1000个并行的“开放通道”。之后,将拒绝新连接(您可以调整此设置)。因此,如果你有——比如说——20台 Webserver,每台上有100个客户端,而每台服务器每小时只有一个页面访问权限,简单的数学计算就会告诉你,你需要2000个到数据库的并行连接。没用的。

因此: 只对有大量请求的应用程序使用它。

在我的测试中,到本地主机的连接时间超过一秒钟,因此假设我应该使用持久连接。进一步的测试表明这是“ localhost”的问题:

以秒为单位的测试结果(以 php 微时间计算) :

  • 托管 web: ConnectDB: 0.0038912296295166
  • 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));

我只是想知道部分解决方案是否应该有一个 use-once 连接池。您可以花时间创建一个连接池时,系统是在低使用率,达到一定的限制,分发和杀死他们时,他们已经完成或超时。在后台,你创造新的连接,因为他们正在采取。在最坏的情况下,假设建立链接是一个限制因素,那么这个过程应该只有在没有连接池的情况下创建连接才是缓慢的?