持久的數據庫連接是指在腳本結束運行時(shí)不關(guān)閉的連接。當收到一個(gè)持久連接的請求時(shí)。PHP 將檢查是否已經(jīng)存在一個(gè)(前面已經(jīng)開(kāi)啟的)相同的持久連接。如果存在,將直接使用這個(gè)連接;如果不存在,則建立一個(gè)新的連接。所謂“相同”的連接是指用相同的用戶(hù)名和密碼到相同主機的連接。
對 web 服務(wù)器的工作和分布負載沒(méi)有完全理解的讀者可能會(huì )錯誤地理解持久連接的作用。特別的,持久連接不會(huì )在相同的連接上提供建立“用戶(hù)會(huì )話(huà)”的能力,也不提供有效建立事務(wù)的能力。實(shí)際上,從嚴格意義上來(lái)講,持久連接不會(huì )提供任何非持久連接無(wú)法提供的特殊功能。
為什么?
這和 web 服務(wù)器工作的方式有關(guān)。web 服務(wù)器可以用三種方法來(lái)利用 PHP 生成 web 頁(yè)面。
第一種方法是將 PHP 用作一個(gè)單獨運行的語(yǔ)言解釋器(CGI Wapper)。以這種方法運行,PHP 會(huì )為向 web 服務(wù)器提出的每個(gè) PHP 頁(yè)面請求生成并結束一個(gè) PHP解釋器線(xiàn)程。由于該線(xiàn)程會(huì )隨每個(gè)請求的結束而結束,因此任何在這個(gè)線(xiàn)程中利用的任何資源(例如指向SQL 數據庫服務(wù)器的連接)都會(huì )隨線(xiàn)程的結束而關(guān)閉。在這種情況下,使用持久連接和非持久連接沒(méi)有任何區別——因為PHP腳本本身的執行不是持久的。
第二,也是最常用的方法,是把 PHP 用作多進(jìn)程 web 服務(wù)器的一個(gè)模塊,這種方法目前只適用于 Apache。對于一個(gè)多進(jìn)程的服務(wù)器,其典型特征是有一個(gè)父進(jìn)程和一組子進(jìn)程協(xié)調運行,其中實(shí)際生成 web 頁(yè)面的是子進(jìn)程。每當客戶(hù)端向父進(jìn)程提出請求時(shí),該請求會(huì )被傳遞給還沒(méi)有被其它的客戶(hù)端請求占用的子進(jìn)程。這也就是說(shuō)當相同的客戶(hù)端第二次向服務(wù)端提出請求時(shí),它將有可能被一個(gè)不同的子進(jìn)程來(lái)處理。在開(kāi)啟了一個(gè)持久連接后,所有請求 SQL 服務(wù)的后繼頁(yè)面都能夠重用這個(gè)已經(jīng)建立的 SQL Server 連接。
最后一種方法是將 PHP 用作多線(xiàn)程 web 服務(wù)器的一個(gè)插件。目前 PHP 4 已經(jīng)支持 ISAPI、WSAPI 和 NSAPI(在非Windows 環(huán)境下),這些使得 PHP 可以被用作諸如 Netscape FastTrack (iPlanet)、Microsoft's Internet Information Server (IIS) 和 O'Reilly's WebSite Pro 等多線(xiàn)程 web 服務(wù)器的插件。持久連接的行為和前面所描述的多過(guò)程模型在本質(zhì)上是相同的。注意 PHP 3 不支持 SAPI。
如果持久連接并沒(méi)有任何附加的功能,那么使用它有什么好處?
答案非常簡(jiǎn)單——效率。當Web Server創(chuàng )建到SQL服務(wù)器的連接耗費(Overhead)較高(如耗時(shí)較久,消耗臨時(shí)內存較多)時(shí),持久連接將更加高效。Overhead高低取決于很多因素。例如,數據庫的種類(lèi),數據庫服務(wù)和web 服務(wù)是否在同一臺服務(wù)器上,SQL 服務(wù)器負載狀況等。當Overhead較高,每次創(chuàng )建數據庫連接成本較高時(shí),持久連接將顯著(zhù)的提高效率。它使得每個(gè)子進(jìn)程在其生命周期中只做一次連接操作,而非每次在處理一個(gè)頁(yè)面時(shí)都要向SQL 服務(wù)器提出連接請求。這也就是說(shuō),每個(gè)子進(jìn)程將對服務(wù)器建立各自獨立的持久連接。例如,如果有 20 個(gè)不同的子進(jìn)程運行某腳本建立了持久的 SQL 服務(wù)器持久連接,那么實(shí)際上向該 SQL 服務(wù)器建立了 20 個(gè)不同的持久連接,每個(gè)進(jìn)程占有一個(gè)。
注意,如果持久連接的子進(jìn)程數目超過(guò)了設定的數據庫連接數限制,系統將會(huì )產(chǎn)生一些問(wèn)題。如果數據庫的同時(shí)連接數限制為 16,而在繁忙會(huì )話(huà)的情況下,有 17 個(gè)線(xiàn)程試圖連接,那么有一個(gè)線(xiàn)程將無(wú)法連接。如果這個(gè)時(shí)候,在腳本中出現了使得連接無(wú)法關(guān)閉的錯誤(例如無(wú)限循環(huán)),則該數據庫的 16 個(gè)連接將迅速地受到影響。請查閱使用的數據庫的文檔,以獲取關(guān)于如何處理已放棄的及閑置的連接的方法。
在使用持久連接時(shí)還有一些特別的問(wèn)題需要注意。例如在持久連接中使用數據表鎖時(shí),如果腳本不管什么原因無(wú)法釋放該數據表鎖,其隨后使用相同連接的腳本將會(huì )被持久的阻塞,使得需要重新啟動(dòng) httpd 服務(wù)或者數據庫服務(wù)。另外,在使用事務(wù)處理時(shí),如果腳本在事務(wù)阻塞產(chǎn)生前結束,則該阻塞也會(huì )影響到使用相同連接的下一個(gè)腳本。不管在什么情況下,都可以通過(guò)使用 register_shutdown_function() 函數來(lái)注冊一個(gè)簡(jiǎn)單的清理函數來(lái)打開(kāi)數據表鎖,或者回滾事務(wù)?;蛘吒玫奶幚矸椒?,是不在使用數據表鎖或者事務(wù)處理的腳本中使用持久連接,這可以從根本上解決這個(gè)問(wèn)題(當然還可以在其它地方使用持久連接)。
以下是一點(diǎn)重要的總結。持久連接與常規的非持久連接應該是可以互相替換的。即將持久連接替換為非持久連接時(shí),你的腳本的行為不應該發(fā)生改變。使用持久連接只應該改變腳本的效率,不應該改變其行為!
參見(jiàn) fbsql_pconnect(),ibase_pconnect(),ifx_pconnect(),ingres_pconnect(),msql_pconnect(),mssql_pconnect(),mysql_pconnect(),ociplogon(),odbc_pconnect(),ora_plogon(),pfsockopen(),pg_pconnect() 和 sybase_pconnect()。