C實現PHP的MySQL數據庫連接池

Mon9015 8年前發布 | 5K 次閱讀 C/C++ django,python
一、導語

連接池在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線程與進程的工作模式

(一)多進程工作模式

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

返回warning的,是由于連接池資源被占用完,會進入等待。如果1.5s仍沒有等待到資源,則返回dbpool is exhushed。
 本文由用戶 Mon9015 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!