2024 年 PHP 日本大会

持久化数据库连接

持久连接是在脚本执行结束时不会关闭的连接。当请求持久连接时,PHP 会检查是否已经存在相同的持久连接(从之前保持打开状态的连接),如果存在,则使用它;如果不存在,则创建连接。“相同”的连接是指连接到同一主机,使用相同的用户名和密码(如果适用)。

不完全了解 Web 服务器工作方式和负载分配的人可能会误解持久连接。特别是,它们并不能让你在同一连接上打开“用户会话”,也不能让你有效地构建事务,也不能做很多其他事情。事实上,为了非常清楚地说明这个问题,持久连接不会提供任何非持久连接无法提供的功能。

为什么?

这与 Web 服务器的工作方式有关。Web 服务器可以使用 PHP 生成网页的方式主要有三种。

第一种方法是将 PHP 用作 CGI “包装器”。以这种方式运行时,每个页面请求(针对 PHP 页面)都会创建和销毁一个 PHP 解释器的实例。因为它在每次请求后都会被销毁,所以它获取的任何资源(例如与 SQL 数据库服务器的连接)都会在它被销毁时关闭。在这种情况下,尝试使用持久连接没有任何好处——它们根本不会持久。

第二种也是最常用的方法是将 PHP 作为多进程 Web 服务器中的模块运行,目前仅包括 Apache。多进程服务器通常有一个进程(父进程)协调一组实际负责提供网页的进程(子进程)。当来自客户端的请求到来时,它会被交给一个尚未为其他客户端服务的子进程。这意味着当同一客户端第二次向服务器发出请求时,它可能由与第一次不同的子进程提供服务。打开持久连接后,每个后续请求 SQL 服务的页面都可以重用与 SQL 服务器建立的相同连接。

最后一种方法是将 PHP 用作多线程 Web 服务器的插件。目前 PHP 支持 WSAPI 和 NSAPI(在 Windows 上),这些都允许 PHP 用作 Netscape FastTrack (iPlanet)、Microsoft 的 Internet Information Server (IIS) 和 O'Reilly 的 WebSite Pro 等多线程服务器上的插件。其行为与前面描述的多进程模型基本相同。

如果持久连接没有任何附加功能,那么它们有什么用呢?

答案非常简单——效率。如果与 SQL 服务器建立连接的开销很高,则持久连接很有用。此开销是否真的很高取决于许多因素。例如,数据库的类型、它是否位于 Web 服务器所在的同一台计算机上、SQL 服务器所在的机器的负载情况等等。底线是,如果连接开销很高,持久连接可以显著提高效率。它们使子进程在其整个生命周期内只需连接一次,而不是每次处理需要连接到 SQL 服务器的页面时都连接一次。这意味着每个打开持久连接的子进程都将拥有自己与服务器的打开的持久连接。例如,如果您有 20 个不同的子进程运行一个脚本,该脚本与您的 SQL 服务器建立持久连接,那么您将有 20 个不同的与 SQL 服务器的连接,每个子进程一个。

但是,请注意,如果您使用的是连接限制被持久子连接超过的数据库,这可能会有一些缺点。如果您的数据库同时连接数限制为 16,而在繁忙的服务器会话过程中,有 17 个子线程尝试连接,则其中一个将无法连接。如果您的脚本中存在阻止连接关闭的错误(例如无限循环),则只有 16 个连接的数据库可能会很快被淹没。请查看您的数据库文档,了解有关处理放弃或空闲连接的信息。

警告

使用持久连接时,还有一些额外的注意事项需要记住。一个是在持久连接上使用表锁定,如果脚本由于某种原因无法释放锁,那么随后使用相同连接的脚本将无限期阻塞,并且可能需要重新启动 httpd 服务器或数据库服务器。另一个是使用事务时,如果脚本执行在事务块结束之前结束,则事务块也将延续到使用该连接的下一个脚本。无论哪种情况,您都可以使用 register_shutdown_function() 注册一个简单的清理函数来解锁表或回滚事务。更好的方法是完全避免这个问题,不要在使用表锁或事务的脚本中使用持久连接(您仍然可以在其他地方使用它们)。

重要总结。持久连接的设计目的是与常规连接一一映射。这意味着您 *始终* 可以用非持久连接替换持久连接,这不会改变脚本的行为。它 *可能*(并且很可能)会改变脚本的效率,但不会改变其行为!

另请参阅 ibase_pconnect()ociplogon()odbc_pconnect()oci_pconnect()pfsockopen()pg_pconnect()

添加备注

用户贡献的备注 10 条备注

Tom
14 年前
PHP 还存在第三种情况:在 fastCGI 接口上运行。在这种情况下,PHP 进程在每次请求后不会被销毁,因此持久连接确实会持久存在。设置 PHP_FCGI_CHILDREN << mysql 的 max_connections,你将一切顺利。
php at alfadog dot net
10 年前
关于 odbc_pconnect 和可能其他 pconnect 变体的附加说明

如果连接遇到错误(错误的 SQL、不正确的请求等),则该错误将出现在 odbc_errormsg 中,用于该连接的每个后续操作,即使后续操作不会导致另一个错误。

例如

脚本使用 odbc_pconnect 连接。
连接在其首次使用时创建。
脚本调用查询“Select * FROM Table1”。
Table1 不存在,odbc_errormsg 包含该错误。

稍后(几天后,也许),使用相同的 odbc_pconnect 参数调用不同的脚本。
连接已存在,因此它被重用。
脚本调用查询“Select * FROM Table0”。
查询运行正常,但 odbc_errormsg 仍然返回有关 Table1 不存在的错误。

我没有看到使用 odbc_ 函数清除该错误的方法,因此请注意这个陷阱,或者使用 odbc_connect。
ynzhang from lakeheadu canada
15 年前
使用 pg_pconnect() 函数似乎不会持久化临时视图/表。因此,如果您尝试使用查询结果创建临时视图/表,然后在同一会话的下一个脚本中访问它们,那么您将无法成功。这些临时视图/表在每个 PHP 脚本结束之后都会消失。解决此问题的一种方法是创建带有会话 ID 作为名称一部分的真实视图/表,并在公共表中记录名称和创建时间。可以使用一个垃圾回收脚本删除会话已过期的视图/表。
pacerier at gmail dot com
9年前
其他人注意到最后一段与上面所有内容相矛盾吗?

(缓存页面: https://archive.is/ZAOwy )
ambrish at php dot net
14 年前
在 IBM_DB2 扩展 v1.9.0 或更高版本中,会在请求结束时对持久连接执行事务回滚,从而结束事务。如果脚本执行在事务块结束之前结束,这将阻止事务块延续到使用该连接的下一个请求。
christopher dot jones at oracle dot com
17年前
对于 oci8 扩展,以下说法并不正确:“[...]当使用事务时,如果脚本执行在事务块结束之前结束,事务块也将延续到使用该连接的下一个脚本”。oci8 扩展在使用持久连接的脚本结束时执行回滚,从而结束事务。回滚还会释放锁。但是,对持久连接的任何 ALTER SESSION 命令(例如更改日期格式)都将保留到下一个脚本。
andy at paradigm-reborn dot com
17年前
对于使用 MySQL 并发现许多剩余休眠进程的用户,请查看 MySQL 的 wait_timeout 指令。默认情况下,它设置为 8 小时,但几乎任何像样的生产服务器都会将其降低到 60 秒的范围。即使在我的测试服务器上,我也遇到了由于剩余的持久连接而导致连接过多的问题。
jean_christian at myrealbox dot com
22年前
如果有人想知道为什么空闲数据库进程(打开的连接)的数量似乎在增加,即使您正在使用持久连接,原因如下:

“您可能正在使用多进程 Web 服务器,例如 Apache。由于
数据库连接不能在不同的进程之间共享,因此如果请求恰好到达不同的 Web 服务器
子进程,则会创建一个新的连接。
连接。”
fabio
18年前
实际上,您可以为连接提供端口,请查看 http://de2.php.net/manual/en/function.mysql-pconnect.php#AEN101879

只需对服务器地址使用“主机名:端口”即可。
RQuadling at GMail dot com
18年前
如果您在同一服务器上有多个数据库,并且正在使用持久连接,则必须在所有表名前缀上加上特定的数据库名称。

使用 xxx_select_db 函数更改数据库会更改所有共享该连接的用户(假设 PHP 正在共享运行而不是 CGI/CLI)的连接的数据库。

如果您有两个数据库(实时数据库和存档数据库),并且您的脚本同时与这两个数据库进行通信,则不能使用两个持久连接并为每个连接更改数据库。

即使您没有指定要使用持久连接,内部也会使用持久连接。这就是为什么在 mysql_connect/mssql_connect (PHPV4.2.0+) 中添加 new_link 的原因。
To Top