PHP 的第三種情況是:在 fastCGI 介面上執行。在這種情況下,PHP 程序在每次請求後*不會*被銷毀,因此持續性連線確實會持續存在。設定 PHP_FCGI_CHILDREN << mysql 的 max_connections,您就沒問題了。
持久性連線是指在腳本執行結束時不會關閉的連結。當請求持久性連線時,PHP 會檢查是否已經存在相同的持久性連線(從先前保持開啟) - 如果存在,則使用它。如果不存在,則建立連結。「相同」連線是指使用相同的使用者名稱和相同的密碼(如果適用)開啟到相同主機的連線。
不熟悉網頁伺服器運作和負載分配方式的人可能會誤解持續性連線的功能。特別是,它們*並非*讓您能在同一個連結上開啟「使用者工作階段」,也*並非*讓您能有效率地建立交易,它們還有很多其他的事情做不到。事實上,更明確地說,持續性連線*不會*提供任何非持續性連線原本就無法提供的功能。
為什麼?
這與網頁伺服器的運作方式有關。網頁伺服器有三種方式可以利用 PHP 產生網頁。
第一種方法是將 PHP 用作 CGI「包裝器」。以這種方式運行時,網頁伺服器會針對每個頁面請求(針對 PHP 頁面)建立和銷毀 PHP 直譯器的一個執行個體。由於它在每次請求後都會被銷毀,因此它獲取的任何資源(例如與 SQL 資料庫伺服器的連結)都會在其銷毀時關閉。在這種情況下,嘗試使用持續性連線並不會帶來任何好處——它們根本不會持續存在。
第二種,也是最常用的方法,是在多程序網頁伺服器中將 PHP 作為模組運行,目前這僅包含 Apache。多程序伺服器通常有一個程序(父程序)協調一組程序(子程序),而這些子程序實際負責提供網頁的工作。當客戶端發出請求時,它會被交給一個尚未服務其他客戶端的子程序。這表示當同一個客戶端向伺服器發出第二個請求時,處理它的子程序可能與第一次不同。當開啟持續性連線時,每個後續請求 SQL 服務的頁面都可以重複使用與 SQL 伺服器建立的相同連線。
最後一種方法是將 PHP 用作多執行緒網頁伺服器的外掛程式。目前 PHP 支援 WSAPI 和 NSAPI(在 Windows 上),它們都允許 PHP 作為外掛程式用於多執行緒伺服器,例如 Netscape FastTrack (iPlanet)、Microsoft 的網際網路資訊服務 (IIS) 和 O'Reilly 的 WebSite Pro。其行為與前面描述的多程序模型基本相同。
如果持續性連線沒有任何額外的功能,那它們有什麼用處呢?
答案非常簡單——效率。如果建立與 SQL 伺服器的連結的負荷很高,那麼持續性連線就很有用。這個負荷是否真的高取決於許多因素。例如,資料庫的類型、它是否與網頁伺服器位於同一台電腦上、SQL 伺服器所在機器的負載情況等等。重點是,如果連線負荷很高,持續性連線會對您有很大的幫助。它們使子程序在其整個生命週期中只需連線一次,而不是每次處理需要連線到 SQL 伺服器的頁面時都進行連線。這表示每個開啟持續性連線的子程序都將擁有自己與伺服器開啟的持續性連線。例如,如果您有 20 個不同的子程序運行一個建立與 SQL 伺服器持續性連線的指令碼,那麼您將擁有 20 個與 SQL 伺服器的不同連線,每個子程序一個。
不過,請注意,如果您使用的資料庫有連線限制,而持續性子連線超過了這個限制,則可能會有一些缺點。如果您的資料庫限制為 16 個同步連線,並且在忙碌的伺服器工作階段中,有 17 個子執行緒嘗試連線,則其中一個將無法連線。如果您的腳本中有錯誤導致連線無法關閉(例如無限迴圈),則只有 16 個連線的資料庫可能會很快被淹沒。請查看您的資料庫文件,以取得處理已放棄或閒置連線的相關資訊。
使用持續性連線時,還有一些額外的注意事項。一是當在持續性連線上使用資料表鎖定時,如果腳本由於某種原因無法釋放鎖定,則使用相同連線的後續腳本將無限期地阻塞,並且可能需要您重新啟動 httpd 伺服器或資料庫伺服器。另一個是當使用交易時,如果腳本執行在交易區塊完成之前結束,則交易區塊也會延續到使用該連線的下一個腳本。在這兩種情況下,您都可以使用 register_shutdown_function() 註冊一個簡單的清除函數來解鎖您的資料表或回復您的交易。更好的做法是,完全避免在使用資料表鎖定或交易的腳本中使用持續性連線(您仍然可以在其他地方使用它們)。
一個重要的總結。持續性連線的設計目的是與一般連線一對一對應。這意味著您應該*總是*能夠用非持續性連線取代持續性連線,而且這不會改變您的腳本的行為。它*可能*(而且很可能會)改變腳本的效率,但不會改變它的行為!
另請參閱 ibase_pconnect()、ociplogon()、odbc_pconnect()、oci_pconnect()、pfsockopen() 和 pg_pconnect()。
PHP 的第三種情況是:在 fastCGI 介面上執行。在這種情況下,PHP 程序在每次請求後*不會*被銷毀,因此持續性連線確實會持續存在。設定 PHP_FCGI_CHILDREN << mysql 的 max_connections,您就沒問題了。
關於 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。
使用 pg_pconnect() 似乎不會保留暫存檢視表/資料表。因此,如果您嘗試使用查詢結果創建暫存檢視表/資料表,然後在同一個工作階段的下一個腳本中存取它們,那您就沒轍了。這些暫存檢視表/資料表會在每個 PHP 腳本結束後消失。解決此問題的一種方法是使用工作階段 ID 作為名稱的一部分創建真實的檢視表/資料表,並將名稱和創建時間記錄在一個通用資料表中。使用一個垃圾收集腳本刪除工作階段過期的檢視表/資料表。
在 IBM_DB2 擴充套件 v1.9.0 或更高版本中,會在請求結束時對持續連線執行交易回滾,從而結束交易。這可以防止交易區塊在腳本執行結束但交易區塊尚未結束的情況下,延續到下一個使用該連線的請求。
對於 oci8 擴充套件來說,「[...] 當使用交易時,如果腳本執行在交易區塊結束之前結束,則交易區塊也會延續到下一個使用該連線的腳本」的說法是不正確的。oci8 擴充套件會在使用持續連線的腳本結束時執行回滾,從而結束交易。回滾也會釋放鎖定。但是,在持續連線上執行的任何 ALTER SESSION 命令(例如更改日期格式)都將保留到下一個腳本。
對於那些使用 MySQL 並發現許多閒置的睡眠進程的人,請查看 MySQL 的 wait_timeout 指令。預設情況下,它設定為 8 小時,但幾乎所有正常的生產伺服器都會將其降低到 60 秒左右。即使在我的測試伺服器上,我也遇到了來自剩餘持續連線的過多連線問題。
如果有人想知道為什麼即使您使用持續連線,閒置資料庫進程(開啟的連線)的數量似乎仍在增長,原因如下:
「您可能正在使用多進程網路伺服器,例如 Apache。由於
資料庫連線無法在不同進程之間共用,因此如果請求恰好來自不同的網路伺服器
子進程,則會創建一個新的連線。」
事實上,您可以為連線提供埠號,參考 http://de2.php.net/manual/en/function.mysql-pconnect.php#AEN101879
只需將伺服器地址設定為「主機名稱:埠號」即可。
如果您在同一台伺服器上有多個資料庫,而且您正在使用持續性連線,則您**必須**在所有資料表名稱前加上特定資料庫名稱作為前綴。
使用 xxx_select_db 函式更改資料庫會改變所有共享該連線的使用者之連線的資料庫(假設 PHP 採用共享模式執行,而不是 CGI/CLI)。
如果您有 2 個資料庫(live 和 archive),並且您的腳本正在與這兩個資料庫通訊,則您不能使用 2 個持續性連線並分別更改每個資料庫。
在內部,即使您沒有明確指定要使用持續性連線,也會使用持續性連線。這就是為什麼在 mysql_connect/mssql_connect (PHPV4.2.0+) 中新增 new_link 的原因。