章 41. 資料庫永久連線

永久的資料庫連線是指在腳本結束運行時不關閉的連線。當收到一個永久連線的請求時。PHP 將檢查是否已經存在一個(前面已經開啟的)相同的永久連線。若果存在,將直接使用這個連線;若果不存在,則建立一個新的連線。所謂「相同」的連線是指用相同的會員名和密碼到相同主電腦的連線。

對 web 伺服器的工作和分佈負載沒有完全理解的讀者可能會錯誤地理解永久連線的作用。特別的,永久連線不會在相同的連線上提供建立「會員會話」的能力,也不提供有效建立事務的能力。實際上,從嚴格意義上來講,永久連線不會提供任何非永久連線無法提供的特殊功能。

為什麼?

這和 web 伺服器工作的模式有關。web 伺服器可以用三種方法來利用 PHP 建立 web 頁面。

第一種方法是將 PHP 用作一個「外殼」。以這種方法運行,PHP 會為向 web 伺服器提出的每個 PHP 頁面請求建立並結束一個 PHP 直譯器執行緒。由於該執行緒會隨每個請求的結束而結束,因此任何在這個執行緒中利用的任何資源(例如指向 SQL 資料庫伺服器的連線)都會隨執行緒的結束而關閉。在這種情況下,使用永久連線不會獲得任何地改變--因為它們根本不是永久的。

第二,也是最常用的方法,是把 PHP 用作多程式 web 伺服器的一個模組,這種方法目前只適用於 Apache。對於一個多程式的伺服器,其典型特徵是有一個父程式和一組子程式協調運行,其中實際建立 web 頁面的是子程式。每當用戶端向父程式提出請求時,該請求會被傳遞給還沒有被其它的用戶端請求佔用的子程式。這也就是說當相同的用戶端第二次向服務端提出請求時,它將有可能被一個不同的子程式來處理。在開啟了一個永久連線後,所有請求 SQL 服務的後繼頁面都能夠重新使用這個已經建立的 SQL Server 連線。

最後一種方法是將 PHP 用作多執行緒 web 伺服器的一個外掛程式。目前 PHP 4 已經支援 ISAPI、WSAPI 和 NSAPI(在 Windows 環境下),這些使得 PHP 可以被用作諸如 Netscape FastTrack (iPlanet)、Microsoft's Internet Information Server (IIS) 和 O'Reilly's WebSite Pro 等多執行緒 web 伺服器的外掛程式。永久連線的行為和前面所描述的多過程模型在本質上是相同的。注意 PHP 3 不支援 SAPI。

若果永久連線並沒有任何附加的功能,那麼使用它有什麼好處?

答案非常簡單--效率。當用戶端對 SQL 伺服器的連線請求非常頻繁時,永久連線將更加高效。連線請求頻繁的標準取決於很多因素。例如,資料庫的種類,資料庫服務和 web 服務是否在同一台伺服器上,SQL 伺服器如何加載負載等。但我們至少知道,當連線請求很頻繁時,永久連線將顯著的提高效率。它使得每個子程式在其生命週期中只做一次連線動作,而非每次在處理一個頁面時都要向 SQL 伺服器提出連線請求。這也就是說,每個子程式將對伺服器建立各自獨立的永久連線。例如,若果有 20 個不同的子程式運行某腳本建立了永久的 SQL 伺服器永久連線,那麼實際上向該 SQL 伺服器建立了 20 個不同的永久連線,每個程式佔有一個。

注意,若果永久連線的子程式數目超過了設定的資料庫連線數限制,系統將會產生一些缺陷。若果資料庫的同時連線數限制為 16,而在繁忙會話的情況下,有 17 個執行緒試圖連線,那麼有一個執行緒將無法連線。若果這個時候,在腳本中出現了使得連線無法關閉的錯誤(例如無限迴圈),則該資料庫的 16 個連線將迅速地受到影響。請查閱使用的資料庫的文件,以取得關於如何處理已放棄的及閒置的連線的方法。

警示

在使用永久連線時還有一些特別的問題需要注意。例如在永久連線中使用資料表鎖時,若果腳本不管什麼原因無法釋放該資料表鎖,其隨後使用相同連線的腳本將會被永久的阻塞,使得需要重新啟動 httpd 服務或是資料庫服務。另外,在使用事務處理時,若果腳本在事務阻塞產生前結束,則該阻塞也會影響到使用相同連線的下一個腳本。不管在什麼情況下,都可以通過使用 register_shutdown_function() 函數來註冊一個簡單的清理函數來開啟資料表鎖,或是返回事務。或是更好的處理方法,是不在使用資料表鎖或是事務處理的腳本中使用永久連線,這可以從根本上解決這個問題(當然還可以在其它地方使用永久連線)。

以下是一點重要的總結。永久連線是為通常連線建立一對一的分佈而設計的。這意味著必須能夠保證在將永久連線置換為非永久連線時,腳本的行為不會改變。使用永久連線將(非常)有可能改變腳本的效率,但不改變其行為!

參見 fbsql_pconnect()ibase_pconnect()ifx_pconnect()ingres_pconnect()msql_pconnect()mssql_pconnect()mysql_pconnect()ociplogon()odbc_pconnect()ora_plogon()pfsockopen()pg_pconnect()sybase_pconnect()