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

在PDO中,可以使用PDO::ATTR_PERSISTENT属性使连接持久化。 根据PHP手册 –

持久性连接在脚本结束时没有closures,但在另一个脚本使用相同凭据请求连接时被caching和重新使用。 持久连接caching允许您避免每次脚本需要与数据库通信时build立新连接的开销,从而导致更快的Web应用程序。

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

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

请务必阅读下面的答案 ,其中详细的方法来缓解这里列出的问题。


使用PDO和其他PHP数据库接口一样,存在着与持久连接相同的缺点:如果脚本在数据库操作过程中意外终止,则下一个获取剩余连接的请求将会从死脚本中断处继续。 连接在进程pipe理器级别保持打开状态(mod_php为Apache,如果使用FastCGI,则为当前的FastCGI进程等),而不是PHP级别,PHP不告诉父进程让连接死亡脚本exception终止。

如果死脚本locking表,这些表将保持locking,直到连接死亡或获取连接的下一个脚本解锁表本身。

如果死脚本处于事务中间,那么可以阻塞大量表,直到死locking时器启动,即使如此,死locking时器也可以杀死较新的请求,而不是造成问题的旧请求。

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

这只是冰山一angular。 它可以在一定程度上通过在每个脚本请求的脏连接之后尝试清理而得到缓解,但是这可能是一个取决于数据库的痛苦。 除非您已经确定创build数据库连接脚本瓶颈的一个方面 (这意味着您已经使用xdebug和/或xhprof完成了代码分析),否则不应将持久连接视为任何解决scheme。

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


澄清一点,我们在工作场所使用持续性连接,但不是select。 我们遇到了奇怪的连接行为,从我们的应用服务器到我们的数据库服务器的初始连接花费三秒钟,而应该花费几分之一秒。 我们认为这是一个内核错误。 我们放弃了尝试排除它,因为它是随机发生的,不能按需复制,我们的外包IT没有具体的跟踪能力。

不pipe什么时候,仓库里的人都在处理几百个传入部分,每个部分都花了三秒半而不是半秒钟,所以我们不得不采取行动才把他们绑架,帮助他们。 所以,我们在我们自己生产的ERP / CRM / CMS怪物中翻了几下,并且经历了所有长久连接的恐惧。 我们花了几个星期的时间来追踪所有看似随意发生的微妙的小问题和奇怪的行为。 事实certificate,我们的用户在应用程序中榨取的那些每周一次的致命错误,就是离开桌面,放弃交易和其他不幸的状态。

这个哭泣的故事有一个重要的意义: 它打破了我们从来没有料到会打破的一切,都是以表演的名义。 权衡是不值得的,我们正在热切等待一天,我们可以切换回正常的连接,而没有来自用户的骚乱。

为了回应Charles的问题,

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

关于持续连接的常见抱怨是,在重新使用之前,其状态不会被重置。 例如,打开和未完成的事务不会自动回滚。 而且,在连接到池中和重用它之间的时间内发生的授权变化没有被反映出来。 这可能被视为一种不必要的副作用。 相反,坚持的名字可以被理解为国家坚持的承诺。

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

mysqli_change_user()函数是一个昂贵的操作。 为获得最佳性能,用户可能需要使用正在设置的编译标志MYSQLI_NO_CHANGE_USER_ON_PCONNECT来重新编译扩展。

用户可以select安全行为和最佳性能。 两者都是有效的优化目标。 为了便于使用,已将安全行为作为默认设置,以最大性能为代价。

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

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

  • 托pipe网站:connectDB:0.0038912296295166
  • localhost:connectDB:1.0214691162109(超过一秒钟:不要使用本地主机!)
  • 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)); 

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

PDO不pipe理持久性。 MySQL驱动程序。 当a)可用并且主机/用户/密码/数据库匹配时,它重新使用连接。 如果有改变,那么它将不会重用连接。 最好的情况是,你拥有的这些连接会经常被启动和停止,因为你在网站上有不同的用户,并且使它们持久化并没有任何好处。

理解持续连接的关键是你不应该在大多数web应用中使用它们。 他们听起来很诱人,但他们很危险,几乎没用。

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

PHP的程序化mysql库有一个function,通过后续调用mysql_connect将返回相同的链接,而不是打开不同的连接(正如人们所期望的那样)。 这与持久连接没有任何关系,并且是特定于mysql库的。 PDO不performance出这种行为


资源链接: 链接

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

是的 ,使用持久连接,如果:

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

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

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

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

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

使用持久连接速度要快得多,尤其是在通过networking访问数据库时。 如果数据库运行在同一台计算机上,它并没有太大的区别,但速度还是比较快的。 但是 – 正如名称所述 – 连接是持久的,即保持打开状态,即使未使用。

问题是,在“默认configuration”中,MySQL只允许1000个并行“开放通道”。 之后,新的连接被拒绝(你可以调整这个设置)。 因此,如果您有20个Web服务器,每个客户端上有100个客户端,而且每个客户端每小时只有一个页面访问权限,那么简单的math计算将告诉您需要2000个并行连接到数据库。 这是行不通的。

Ergo:只用于有大量请求的应用程序。

持续的连接应该可以带来巨大的性能提升。 我不同意你应该“避免”持久性的观点。

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

在我看来,持续的连接会占用更多的系统资源。 也许是微不足道的,但仍然…

使用持续连接的解释显然是减less了成本相当高的连接数量,尽pipe与其他数据库相比,MySQL的连接速度要快得多。

持续连接的第一个麻烦

如果您每秒创build1000个连接,通常不会确保它保持打开状态的时间很长,但操作系统可以。 基于TCP / IP协议端口不能即时回收,在“FIN”阶段还需要投入一段时间才能被回收。

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

许多人根本没有意识到你可以增加* max_connections *variables,并获得超过100个与MySQL并发的连接,其他人被老的Linux问题殴打,无法传达超过1024个与MySQL的连接。

现在可以谈谈为什么持续连接在mysqli扩展中被禁用。 尽pipe你可以误用长久的连接,并获得不良的performance,这不是主要原因。 实际的原因是 – 你可以得到更多的问题。

当MySQL不是那么困难的时候,持久的连接被放到MySQL 3.22 / 3.23的各种场合,这意味着你可以很容易地回收连接,没有问题。 在后来的版本中,问题的数量是多less? – 如果您回收有未提交事务的连接,就会陷入困境。 如果使用自定义字符集configuration回收连接,则会再次遇到危险,并且可能会对每个会话variables进行转换。

使用持续连接的一个麻烦是它不能很好地扩展。 对于有5000人连接的人,您需要5000个持续连接。 为了消除对持续性的要求,你可能有能力服务于10000个具有相似连接数量的人,因为他们在与他们不在一起时可以分享个人关系。

我只是想知道是否部分解决scheme是有一个使用一次连接池。 当系统处于低使用率时,您可以花费时间创build连接池,达到极限时,将它们发出并在完成或超时时杀死它们。 在后台,您正在创build新的连接,因为他们正在采取。 在最糟糕的情况下,这应该只是像创build没有池的连接一样慢,假设build立连接是限制因素?