轉自: http://rango.swoole.com/archives/265
PHP的數據庫連接池一直以來都是一個難題,很多從PHP語言轉向Java的項目,大多數原因都是因為Java有更好的連接池實現。PHP的MySQL擴展提供了長連接的API,但在PHP機器數量較多,規模較大的情況下,mysql_pconnect非但不能節約MySQL資源,反而會加劇數據庫的負荷。
假設有100台PHP的應用服務器,每個機器需要啟動100個apache或fpm工作進程,那每個進程都會產生一個長連接到MySQL。這一共會產生1萬個My SQL連接。大家都知道MySQL是每個連接會占用1個線程。那MYSQL就需要創建1萬個線程,這樣大量的系統資源被浪費在線程間上下文切換上。而你的業務代碼中並不是所有地方都在做數據庫操作,所以這個就是浪費的。
連接池就不同了,100個worker進程,公用10個數據庫連接即可,當操作完數據庫后,立即釋放資源給其他worker進程。這樣就算有100台PHP的服務器,那也只會創建1000個MySQL的連接,完全可以接受的。
以前確實沒有好的辦法來解決此問題的,現在有了swoole擴展,利用swoole提供的task功能可以很方便做出一個連接池來。
代碼如下:
$serv = new swoole_server("127.0.0.1", 9508); $serv->set(array( 'worker_num' => 100, 'task_worker_num' => 10, //MySQL連接的數量 )); function my_onReceive($serv, $fd, $from_id, $data) { //taskwait就是投遞一條任務,這里直接傳遞SQL語句了 //然后阻塞等待SQL完成 $result = $serv->taskwait("show tables"); if ($result !== false) { list($status, $db_res) = explode(':', $result, 2); if ($status == 'OK') { //數據庫操作成功了,執行業務邏輯代碼,這里就自動釋放掉MySQL連接的占用 $serv->send($fd, var_export(unserialize($db_res), true) . "\n"); } else { $serv->send($fd, $db_res); } return; } else { $serv->send($fd, "Error. Task timeout\n"); } } function my_onTask($serv, $task_id, $from_id, $sql) { static $link = null; if ($link == null) { $link = mysqli_connect("127.0.0.1", "root", "root", "test"); if (!$link) { $link = null; $serv->finish("ER:" . mysqli_error($link)); return; } } $result = $link->query($sql); if (!$result) { $serv->finish("ER:" . mysqli_error($link)); return; } $data = $result->fetch_all(MYSQLI_ASSOC); $serv->finish("OK:" . serialize($data)); } function my_onFinish($serv, $data) { echo "AsyncTask Finish:Connect.PID=" . posix_getpid() . PHP_EOL; } $serv->on('Receive', 'my_onReceive'); $serv->on('Task', 'my_onTask'); $serv->on('Finish', 'my_onFinish'); $serv->start();
這里task_worker_num就是要啟用的數據庫連接池數量,worker進程為100時,連接池數量為10就可以設置為worker_num = 100, task_worker_num = 10。
