C實現PHP的MySQL數據庫連接池
連接池在JAVA中應用的很廣泛,而在PHP中很少使用。
以Mysql為例,JAVA中使用連接池的原因之一是避免重復新建、釋放連接資源帶來的開銷。而在PHP中都是直連,因為這部分開銷對于C API而言性能上是沒有問題的。
那么既然PHP直連已經沒有性能問題,那為何還要多次一舉開發mysql的數據庫連接池擴展呢?因為根本的目的是在于理解PHP的TS(Tthread Safe線程安全)與NTS(Not Thread Safe非線程安全)運行模式,而并非數據庫連接池這個功能。
二、原理連接池的基本思想是在系統加載時,初始化默認數量的連接對象存儲在內存中,當有客戶端需要訪問數據庫時,根據場景選擇是分配、新建、等待、失敗返回連接對象。使用完畢之后,連接將被重新放置回連接池中等待下一個請求的再分配,而不是釋放內存。
連接池中連接的建立、釋放都由連接池自身來管理,同時可以通過設置初始化最小連接數、最大連接數、最大空閑時間等來配置連接池。
注:在此擴展中,提供了最小連接數(min_connection)、最大連接數(max_connection)的設置
三、實現思路1.定義全局數組變量dbpools
dbpools中的元素類型為
struct _mysql_node{ MYSQL *mysql; //連接資源句柄 int is_used; //標記是否被占用 int result_id; //記錄my_mysql_query的查詢結果集 } mysql_node;
2.通過獲取配置文件中設置的min_connection初始化dbpools
3.定義全局變量db_pools_length(目前擁有鏈接數)、db_pools_use_length(目前被使用的鏈接數),通過這兩個值來確定分配資源的情景
注:正因為使用全局變量實現連接池,通過修改全局變量的狀態來選擇資源連接,決定了該擴展必須運行在共享全局變量的ZTS(Zend Thread Safe)線程安全模式下。(例如IIS或Apache MPM Worker模式)
如果希望支持多進程,可以通過進程間通信來設置全局變量,實現線程池。
實現思路圖:
圖片:1.jpg

(一)多進程工作模式
PHP的多進程工作模式以Apache apxs舉例。
apache啟動時,會fork出N個子進程用來等待接受處理客戶端的請求。進程之間相互隔離,全局變量也無法直接訪問(可以通過進程間通信訪問)。這樣的好處是能夠保證PHP環境的長時間穩定,
即使有部分進程因內存泄漏而崩潰也不會影響其他進程。由于PHP相當于粘合劑,它實際相當于集合多個庫的API,例如集合了libcurl、libmemcache、libzip等,要保證所有的庫都正常運行是比較困難的。
那么為了保證PHP的高可靠性,這種多進程的模式就是首選。
圖片:2.jpg

(二)多線程工作模式
PHP的多線程工作模式以IIS舉例。
這種模式下,只有一個進程在后臺運行著,所有請求都是通過這一個進程來完成。只是在處理每個請求時,會創建獨立的線程來處理。
正是因為使用多線程這種模式,可以直接訪問全局變量得以方便的實現數據庫連接池。
圖片:3.jpg

1.widnwos下IIS Server或Apache多線程模式,php5.3.*
2.修改php.ini,添加
[my_mysql]
my_mysql.host = localhost
my_mysql.user = root
my_mysql.password =
my_mysql.port = 3306
my_mysql.max_connection = 200 //最大連接數
my_mysql.min_connection = 100 //默認最小連接數
看到這里的配置,能看出來這個數據庫連接池擴展沒有實現多數據源的連接池。因為目的不在連接池本身,所以也沒有特地去寫多數據源的功能。
3.修改php.ini,添加
extension=php_my_mysql.dll
4.重啟apache server
七、相關下載擴展dll下載: php_my_mysql.dll
源碼下載: http://yunpan.cn/QWmEN8PuuRVYN
擴展測試結果測試配置:
[my_mysql]
my_mysql.max_connection = 2 //最大連接數
my_mysql.min_connection = 1 //默認最小連接數
使用三個瀏覽器,運行下列測試腳本:
<?php /** * 從數據庫連接池中取得一個鏈接資源 * 可能產生如下情景 * 1.如果有空閑連接則直接返回鏈接資源 * 2.如果沒有空閑連接,并且連接數沒有超過最大連接數,則擴充連接池并返回新建的鏈接資源 * 3.如果沒有空閑連接同時已經是最大連接數,則進入等待,超過1.5s仍沒有空閑資源則超時返回NULL. * * 失敗返回NULL */ $db = my_mysql_get_conn(); if($db === false){ exit('dbpool is exhushed.'); } /** * 選擇數據庫 * @param resource $db * @param string $db_name 數據庫名稱 */ my_mysql_select_db($db, 'test'); /** * ping數據庫 */ my_mysql_ping($db); /** * 執行SQL語句,可以執行INSERT、SHOW、SELECT、UPDATE、DELETE等語句 * @param resource $db * @param string $sql SQL */ my_mysql_query($db, "INSERT INTO test VALUES(id, 'dbpool', 'test dbpool')"); // 獲取上一條INSERT語句返回的自增ID $id = my_mysql_get_insert_id($db); echo $id; $result = my_mysql_query($db, "SELECT * FROM test"); /** * 將查詢結果轉換為二維數組 * @param resource $db */ $arr = my_mysql_fetch_assoc($result); print_r($arr); sleep(3); var_dump($db);//sleep之后,必須輸出$db,否則會因為PHP的解析優化,sleep過程中已經將$db釋放,就沒有辦法測試連接被占用的情況。
測試結果圖:
圖片:4.jpg
