在 PDO 中,可以使用 PDO::ATTR_PERSISTENT
属性使连接保持持久。根据 php 手册 -
持久连接不会在脚本结束时关闭,而是
当另一个脚本请求连接时,会被缓存并重新使用
相同的凭据。持久连接缓存允许您
避免每次建立新连接的开销
脚本需要与数据库对话,从而获得更快的网络速度
应用程序。
该手册还建议在使用 PDO ODBC 驱动程序时不要使用持久连接,因为它可能会妨碍 ODBC 连接池过程。
显然,除了最后一种情况之外,在 PDO 中使用持久连接似乎没有任何缺点。但是,我想知道使用这种机制是否还有其他缺点,即这种机制会导致性能下降或类似的情况。
In PDO, a connection can be made persistent using the PDO::ATTR_PERSISTENT
attribute. According to the php manual -
Persistent connections are not closed at the end of the script, but
are cached and re-used when another script requests a connection using
the same credentials. The persistent connection cache allows you to
avoid the overhead of establishing a new connection every time a
script needs to talk to a database, resulting in a faster web
application.
The manual also recommends not to use persistent connection while using PDO ODBC driver, because it may hamper the ODBC Connection Pooling process.
So apparently there seems to be no drawbacks of using persistent connection in PDO, except in the last case. However., I would like to know if there is any other disadvantages of using this mechanism, i.e., a situation where this mechanism results in performance degradation or something like that.
发布评论
评论(8)
请务必阅读下面的答案,其中详细介绍了缓解此处列出的问题的方法。
与任何其他执行持久连接的 PHP 数据库接口一样,使用 PDO 也存在同样的缺点:如果您的脚本在数据库操作过程中意外终止,则获取剩余连接的下一个请求将从失效脚本停止的位置继续。连接在进程管理器级别(Apache for mod_php,当前的 FastCGI 进程,如果您使用 FastCGI 等)保持打开状态,而不是在 PHP 级别,并且 PHP 不会告诉父进程让连接在以下情况终止:脚本异常终止。
如果死脚本锁定了表,则这些表将保持锁定状态,直到连接终止或获取连接的下一个脚本本身解锁表。
如果死脚本位于事务中间,则可能会阻塞多个表,直到死锁计时器启动为止,即使如此,死锁计时器也可以杀死较新的请求,而不是导致问题的较旧的请求。
如果失效脚本处于事务中间,则获取该连接的下一个脚本也会获取事务状态。很可能(取决于您的应用程序设计)下一个脚本实际上可能不会尝试提交现有事务,或者在不应该提交的时候提交,或者在不应该提交的时候回滚。
这只是冰山一角。通过在每个脚本请求的脏连接后始终尝试清理,可以在一定程度上缓解这种情况,但这可能会很痛苦,具体取决于数据库。除非您已确定创建数据库连接是脚本中的瓶颈之一(这意味着您已使用 xdebug 和/或 xhprof),您应该 < em>不将持久连接视为任何问题的解决方案。
此外,大多数现代数据库(包括 PostgreSQL)都有自己首选的执行连接池的方法,这些方法没有普通的基于 PHP 的持久连接所具有的直接缺点。
为了澄清一点,我们在工作场所使用持久连接,但不是出于选择。我们遇到了奇怪的连接行为,从我们的应用程序服务器到数据库服务器的初始连接花费了整整三秒,而它本应该花费一小部分时间。一秒钟。我们认为这是一个内核错误。我们放弃尝试对其进行故障排除,因为它是随机发生的,无法按需重现,而且我们的外包 IT 没有具体的能力来追踪它。
不管怎样,当仓库里的人正在处理几百个传入的零件,而每个零件需要三秒半而不是半秒时,我们必须在他们绑架我们所有人并让我们帮助他们之前采取行动。因此,我们对我们自己开发的 ERP/CRM/CMS 怪物进行了一些修改,并亲身体验了持久连接的所有可怕之处。我们花了几周的时间来追踪所有看似随机发生的微妙小问题和奇怪行为。事实证明,我们的用户努力从我们的应用程序中挤出的那些每周一次的致命错误导致了锁定的表、放弃的交易和其他不幸的不稳定状态。
这个悲惨的故事有一个道理:它以性能的名义打破了我们从未想过会打破的东西。这种权衡是不值得的,我们热切地等待着有一天我们可以做到这一点切换回正常连接,而不会引起用户的骚乱。
Please be sure to read this answer below, which details ways to mitigate the problems outlined here.
The same drawbacks exist using PDO as with any other PHP database interface that does persistent connections: if your script terminates unexpectedly in the middle of database operations, the next request that gets the left over connection will pick up where the dead script left off. The connection is held open at the process manager level (Apache for mod_php, the current FastCGI process if you're using FastCGI, etc), not at the PHP level, and PHP doesn't tell the parent process to let the connection die when the script terminates abnormally.
If the dead script locked tables, those tables will remain locked until the connection dies or the next script that gets the connection unlocks the tables itself.
If the dead script was in the middle of a transaction, that can block a multitude of tables until the deadlock timer kicks in, and even then, the deadlock timer can kill the newer request instead of the older request that's causing the problem.
If the dead script was in the middle of a transaction, the next script that gets that connection also gets the transaction state. It's very possible (depending on your application design) that the next script might not actually ever try to commit the existing transaction, or will commit when it should not have, or roll back when it should not have.
This is only the tip of the iceberg. It can all be mitigated to an extent by always trying to clean up after a dirty connection on every single script request, but that can be a pain depending on the database. Unless you have identified creating database connections as the one thing that is a bottleneck in your script (this means you've done code profiling using xdebug and/or xhprof), you should not consider persistent connections as a solution to anything.
Further, most modern databases (including PostgreSQL) have their own preferred ways of performing connection pooling that don't have the immediate drawbacks that plain vanilla PHP-based persistent connections do.
To clarify a point, we use persistent connections at my workplace, but not by choice. We were encountering weird connection behavior, where the initial connection from our app server to our database server was taking exactly three seconds, when it should have taken a fraction of a fraction of a second. We think it's a kernel bug. We gave up trying to troubleshoot it because it happened randomly and could not be reproduced on demand, and our outsourced IT didn't have the concrete ability to track it down.
Regardless, when the folks in the warehouse are processing a few hundred incoming parts, and each part is taking three and a half seconds instead of a half second, we had to take action before they kidnapped us all and made us help them. So, we flipped a few bits on in our home-grown ERP/CRM/CMS monstrosity and experienced all of the horrors of persistent connections first-hand. It took us weeks to track down all the subtle little problems and bizarre behavior that happened seemingly at random. It turned out that those once-a-week fatal errors that our users diligently squeezed out of our app were leaving locked tables, abandoned transactions and other unfortunate wonky states.
This sob-story has a point: It broke things that we never expected to break, all in the name of performance. The tradeoff wasn't worth it, and we're eagerly awaiting the day we can switch back to normal connections without a riot from our users.
针对上述 Charles 的问题,
来自: http://www.mysqli.quickstart.connections.php" php.net/manual/en/mysqli.quickstart.connections.php -
In response to Charles' problem above,
From : http://www.php.net/manual/en/mysqli.quickstart.connections.php -
仅当连接到数据库需要(相对)较长时间时,持久连接才是一个好主意。如今,这种情况几乎不再发生。持久连接的最大缺点是它限制了可以浏览站点的用户数量:如果 MySQL 配置为一次仅允许 10 个并发连接,那么当第 11 个人尝试浏览您的站点时,该连接将无法正常工作。
PDO 不管理持久性。 MySQL 驱动程序可以。当 a) 连接可用并且主机/用户/密码/数据库匹配时,它会重用连接。如果发生任何更改,它将不会重用连接。最好的情况是,您所拥有的这些连接将频繁启动和停止,因为站点上有不同的用户,并且使它们持久化并没有任何好处。
了解持久连接的关键是您不应该在大多数 Web 应用程序中使用它们。它们听起来很诱人,但很危险而且几乎没用。
我确信还有其他线程,但持久连接很危险,因为它在请求之间持续存在。例如,如果您在请求期间锁定了一个表,然后无法解锁,那么该表将无限期地保持锁定状态。持久连接对于 99% 的应用程序来说几乎毫无用处,因为您无法知道不同请求之间是否会使用相同的连接。每个 Web 线程都有自己的一组持久连接,您无法控制哪个线程将处理哪些请求。
PHP 的过程 mysql 库有一项功能,即后续调用 mysql_connect 将返回相同的链接,而不是打开不同的连接(正如人们所期望的那样)。这与持久连接无关,是特定于 mysql 库的。 PDO 不会表现出此类行为
资源链接:链接
一般情况您可以将此用作粗略的“规则集”::
是,使用持久连接,如果:
你不会导致 200 个打开的(但可能是空闲的)连接,
因为有200个不同的用户共享在同一台主机上。
数据库正在您访问的另一台服务器上运行
网络
一个(一个)应用程序经常访问数据库
否,不要使用持久连接,如果:
您的应用程序每小时只需要访问数据库 100 次。
您有很多很多的网络服务器访问一台数据库服务器
使用持久连接的速度要快得多,特别是当您通过网络访问数据库时。如果数据库运行在同一台机器上,差别不大,但还是快一点。然而,正如其名称所示,连接是持久的,即即使不使用它,它也保持打开状态。
问题是,在“默认配置”下,MySQL 只允许 1000 个并行“开放通道”。之后,新的连接将被拒绝(您可以调整此设置)。因此,如果您有 20 个 Web 服务器,每台服务器上有 100 个客户端,并且每个服务器每小时仅访问一页,简单的数学计算就会告诉您,您将需要 2000 个与数据库的并行连接。那是行不通的。
因此:仅将其用于有大量请求的应用程序。
Persistent connections are a good idea only when it takes a (relatively) long time to connect to your database. Nowadays that's almost never the case. The biggest drawback to persistent connections is that it limits the number of users you can have browsing your site: if MySQL is configured to only allow 10 concurrent connections at once then when an 11th person tries to browse your site it won't work for them.
PDO does not manage the persistence. The MySQL driver does. It reuses connections when a) they are available and the host/user/password/database match. If any change then it will not reuse a connection. The best case net effect is that these connections you have will be started and stopped so often because you have different users on the site and making them persistent doesn't do any good.
The key thing to understand about persistent connections is that you should NOT use them in most web applications. They sound enticing but they are dangerous and pretty much useless.
I'm sure there are other threads on this but a persistent connection is dangerous because it persists between requests. If, for example, you lock a table during a request and then fail to unlock then that table is going to stay locked indefinitely. Persistent connections are also pretty much useless for 99% of your apps because you have no way of knowing if the same connection will be used between different requests. Each web thread will have it's own set of persistent connections and you have no way of controlling which thread will handle which requests.
The procedural mysql library of PHP, has a feature whereby subsequent calls to mysql_connect will return the same link, rather than open a different connection (As one might expect). This has nothing to do with persistent connections and is specific to the mysql library. PDO does not exhibit such behaviour
Resource Link : link
In General you could use this as a rough "ruleset"::
YES, use persistent connections, if:
you will not result in 200 open (but probably idle) connections,
because there are 200 different users shared on the same host.
The database is running on another server that you are accessing over
the network
An (one) application accesses the database very often
NO, don't use persistent connections, if:
Your application only needs to access the database 100 times an hour.
You have many, many webservers accessing one database server
Using persistent connections is considerable faster, especially if you are accessing the database over a network. It doesn't make so much difference if the database is running on the same machine, but it is still a little bit faster. However - as the name says - the connection is persistent, i.e. it stays open, even if it is not used.
The problem with that is, that in "default configuration", MySQL only allows 1000 parallel "open channels". After that, new connections are refused (You can tweak this setting). So if you have - say - 20 Webservers with each 100 Clients on them, and every one of them has just one page access per hour, simple math will show you that you'll need 2000 parallel connections to the database. That won't work.
Ergo: Only use it for applications with lots of requests.
在我的测试中,我与本地主机的连接时间超过一秒,因此假设我应该使用持久连接。 一
进
有趣的是:以下代码与使用 127.0.0.1 一样快:
On my tests I had a connection time of over a second to my localhost, thus assuming I should use a persistent connection. Further tests showed it was a problem with 'localhost':
Test results in seconds (measured by php microtime):
Interestingly: The following code is just as fast as using 127.0.0.1:
持久连接应该会带来相当大的性能提升。我不同意你应该“避免”持久性的说法。
听起来上面的抱怨是由使用 MyIASM 表并通过获取表锁侵入他们自己的事务版本的人引起的。当然,你会陷入死锁!使用 PDO 的 beginTransaction() 并将表移至 InnoDB。
Persistent connections should give a sizable performance boost. I disagree with the assement that you should "Avoid" persistence..
It sounds like the complaints above are driven by someone using MyIASM tables and hacking in their own versions of transactions by grabbing table locks.. Well of course you're going to deadlock! Use PDO's beginTransaction() and move your tables over to InnoDB..
在我看来,持久连接会消耗更多的系统资源。也许是微不足道的数额,但仍然......
seems to me having a persistent connection would eat up more system resources. Maybe a trivial amount, but still...
使用持久连接的原因显然是减少了相当昂贵的连接数量,尽管与其他数据库相比,MySQL 的连接速度要快得多。
持久连接的第一个麻烦...
如果您每秒创建 1000 个连接,您通常无法确保它保持打开状态很长时间,但操作系统可以。基于TCP/IP协议的端口不能立即回收,还必须在“FIN”阶段等待一段时间才能被回收。
第二个问题...使用大量 MySQL 服务器连接。
许多人根本没有意识到你可以增加 *max_connections* 变量并获得超过 100 个与 MySQL 的并发连接,而其他人则被旧版 Linux 的问题所困扰,即无法与 MySQL 传送超过 1024 个连接。
现在允许讨论为什么在 mysqli 扩展中禁用持久连接。尽管您可能会滥用持久连接并获得较差的性能,但这并不是主要原因。真正的原因是——你可能会遇到更多问题。
在 MySQL 3.22/3.23 的各个时期,持久连接都被引入到 PHP 中,当时 MySQL 并不那么困难,这意味着您可以轻松地回收连接,没有任何问题。然而,在后来的版本中,出现了很多问题——如果您回收具有未提交事务的连接,您就会遇到麻烦。如果您使用自定义字符集配置回收连接,您将再次处于危险之中,并且每个会话变量也可能会被转换。
使用持久连接的一个问题是它的扩展性不太好。对于拥有 5000 人连接的用户,您将需要 5000 个持久连接。为了消除持久性的要求,您可以为 10000 名具有相似数量联系的人提供服务,因为他们能够在不与他们在一起时共享个人联系。
The explanation for using persistent connections is obviously reducing quantity of connects that are rather costly, despite the fact that they're considerably faster with MySQL compared to other databases.
The very first trouble with persistent connections...
If you are creating 1000's of connections per second you normally don't ensure that it stays open for very long time, but Operation System does. Based on TCP/IP protocol Ports can’t be recycled instantly and also have to invest a while in “FIN” stage waiting before they may be recycled.
The 2nd problem... using a lot of MySQL server connections.
Many people simply don't realize you are able to increase *max_connections* variable and obtain over 100 concurrent connections with MySQL others were beaten by older Linux problems of the inability to convey more than 1024 connections with MySQL.
Allows talk now about why Persistent connections were disabled in mysqli extension. Despite the fact that you can misuse persistent connections and obtain poor performance which was not the main reason. The actual reason is – you can get a lot more issues with it.
Persistent connections were put into PHP throughout occasions of MySQL 3.22/3.23 when MySQL was not so difficult which means you could recycle connections easily with no problems. In later versions quantity of problems however came about – Should you recycle connection that has uncommitted transactions you take into trouble. If you recycle connections with custom character set configurations you’re in danger again, as well as about possibly transformed per session variables.
One trouble with using persistent connections is it does not really scale that well. For those who have 5000 people connected, you'll need 5000 persistent connections. For away the requirement for persistence, you may have the ability to serve 10000 people with similar quantity of connections because they are in a position to share individuals connections when they are not with them.
我只是想知道部分解决方案是否是拥有一个一次性连接池。您可以在系统使用率低、达到限制时花时间创建连接池,将它们分发出去,并在它们完成或超时时杀死它们。在后台,您正在创建新的连接。在最坏的情况下,假设建立链接是限制因素,这应该只像在没有池的情况下创建连接一样慢?
I was just wondering whether a partial solution would be to have a pool of use-once connections. You could spend time creating a connection pool when the system is at low usage, up to a limit, hand them out and kill them when either they've completed or timed out. In the background you're creating new connections as they're being taken. At worst case this should only be as slow as creating the connection without the pool, assuming that establishing the link is the limiting factor?