持久数据库连接

持久数据库连接是指在脚本结束运行时不关闭的连接。当收到持久连接的请求时,PHP 将检查是否已经存在相同的持久连接(前面已经开启的)——如果存在,将直接使用这个连接。如果不存在,则建立新的连接。所谓“相同”的连接是指用相同的用户名和密码连接到相同主机的连接。

对 web 服务器的工作方式和负载分配方式没有完全理解可能会错误地理解持久连接。特别是持久连接不会在相同的连接上提供建立“用户会话”的能力,也不提供有效建立事务的能力。实际上,要非常清楚的了解持久连接不会提供任何非持久连接无法实现的功能。

为什么?

这和 web 服务器的工作方式有关。web 服务器可以通过三种方法来利用 PHP 生成 web 页面。

第一种方法是将 PHP 自以为 CGI“包装器”用作一个单独运行的语言解释器(CGI Wapper)。当以这种方法运行时,PHP 会为向 web 服务器的每个 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()