Swoole MySQL實例

2019-08-14 19:13 更新

Swoole異步MySQL介紹

MySQL異步是指將MySQL連接事件驅(qū)動化,這樣就編程了非阻塞IO。使用Swoole可以實現(xiàn)mysql異步鏈接,Mysql連接池等。
  • 異步MySQL客戶端依賴PHP的 mysqlnd 和 mysqli 2個擴展,請使用php -mphpinfo確認PHP是否有這2個擴展。
  • 另外需要在編譯swoole時制定--enable-async-mysql

1.8.6版本已移除對mysqlimysqlnd擴展的依賴,并改為內(nèi)置,無需額外的編譯參數(shù)開啟

簡單實例:

$db = new Swoole\MySQL;
$server = array(
    'host' => '127.0.0.1',
    'user' => 'test',
    'password' => 'test',
    'database' => 'test',
);

$db->connect($server, function ($db, $result) {
    $db->query("show tables", function (Swoole\MySQL $db, $result) {
        if ($result === false) {
            var_dump($db->error, $db->errno);
        } elseif ($result === true) {
            var_dump($db->affected_rows, $db->insert_id);
        } else {
            var_dump($result);
            $db->close();
        }
    });
});

MySQL長連接

MySQL短連接每次請求操作數(shù)據(jù)庫都需要建立與MySQL服務(wù)器建立TCP連接,這是需要時間開銷的。TCP連接需要3次網(wǎng)絡(luò)通信。這樣就增加了一定的延時和額外的IO消耗。請求結(jié)束后會關(guān)閉MySQL連接,還會發(fā)生3/4次網(wǎng)絡(luò)通信。

close操作不會增加響應(yīng)延時,原因是close后是由操作系統(tǒng)自動進行通信的,應(yīng)用程序感知不到

長連接就可以避免每次請求都創(chuàng)建連接的開銷,節(jié)省了時間和IO消耗。提升了PHP程序的性能。

斷線重連

在cli環(huán)境下,PHP程序需要長時間運行,客戶端與MySQL服務(wù)器之間的TCP連接是不穩(wěn)定的。

  • MySQL-Server會在一定時間內(nèi)自動切斷連接
  • PHP程序遇到空閑期時長時間沒有MySQL查詢,MySQL-Server也會切斷連接回收資源
  • 其他情況,在MySQL服務(wù)器中執(zhí)行kill process殺掉某個連接,MySQL服務(wù)器重啟

這時PHP程序中的MySQL連接就失效了。如果仍然執(zhí)行mysql_query,就會報一個“MySQL server has gone away”的錯誤。程序處理不到就直接遇到致命錯誤并退出了。所以PHP程序中需要斷線重連。

有很多人提出了mysql_ping的方案,每次mysql_query進行連接檢測或者定時連接檢測。這個方案不是最好的。原因是

  • mysql_ping需要主動偵測連接,帶來了額外的消耗
  • 定時執(zhí)行mysql_ping不能解決問題,如剛剛執(zhí)行過mysql_ping檢測之后,連接就關(guān)閉了

最佳的方案是,進行斷線重連 。它的原理是:

  1. mysql_query執(zhí)行后檢測返回值
  2. 如果mysql_query返回失敗,檢測錯誤碼發(fā)現(xiàn)為2006/2013(這2個錯誤表示連接失敗),再執(zhí)行一次mysql_connect
  3. 執(zhí)行mysql_connect后,重新執(zhí)行mysql_query,這時必然會成功,因為已經(jīng)重新建立了連接
  4. 如果mysql_query返回成功,那么連接是有效的,這是一次正常的調(diào)用

可參考swoole_framework中的代碼

MySQL異步

MySQL異步是指將MySQL連接事件驅(qū)動化,這樣就編程了非阻塞IO。數(shù)據(jù)庫操作并不會阻塞進程,在MySQL-Server返回結(jié)果時再執(zhí)行對應(yīng)的邏輯。

有幾個點需要注意一下:

  • 異步MySQL并沒有節(jié)省SQL執(zhí)行的時間
  • 一個MySQL連接同時只能執(zhí)行1個SQL,如果異步MySQL存在并發(fā)那么必須創(chuàng)建多個MySQL連接

異步回調(diào)程序中,異步MySQL并沒有提升性能。異步最大的好處是可以高并發(fā),如果并發(fā)1萬個請求,那么就需要建立1萬個MySQL連接,這會給MySQL-Server帶來巨大的壓力。

MySQL是根據(jù)連接數(shù)分配資源的,一個連接需要開啟一個線程。1000連接那么需要維持1000線程才可以。線程數(shù)量增加后,線程間切換會占用大量CPU資源
MySQL短連接反而不會出現(xiàn)此問題,因為短連接在使用完后就釋放了。不會占用MySQL-Server的連接資源

雖然應(yīng)用層代碼使用異步回調(diào)避免了自身的阻塞,實際上真正的瓶頸是數(shù)據(jù)庫服務(wù)器。異步MySQL還帶來了額外的編程復(fù)雜度,所以除非是特殊場景的需求,否則不建議使用異步MySQL。

如果程序中堅持要使用異步,那么必須是異步MySQL+連接池的形式。超過規(guī)定的MySQL最大連接后,應(yīng)當對SQL請求進行排隊,而不是創(chuàng)建新連接,避免大量并發(fā)請求導(dǎo)致MySQL服務(wù)器崩潰。

MySQL連接池

連接池是可以有效降低MySQL-Server負載的。原理是 連接池使用一個共享資源的模式,如并發(fā)100個請求,實際上并不是每個請求的所有時間都在執(zhí)行SQL查詢。這樣100個請求,共享20個MySQL連接就可以滿足需求了。當一個請求操作完數(shù)據(jù)庫后,開始進入模板渲染等流程,這時就會釋放數(shù)據(jù)庫連接給其他的請求使用。

連接池僅在超大型應(yīng)用中才有價值。普通的應(yīng)用采用MySQL長連接方案,每個php-fpm創(chuàng)建一個MySQL連接,每臺機器開啟100個php-fpm進程。如果有10臺機器,每臺機器并發(fā)的請求為100。實際上只需要創(chuàng)建1000個MySQL連接就能滿足需求,數(shù)據(jù)庫的壓力并不大。即使有100臺機器,硬件配置好的存儲服務(wù)器依然可以承受。

達到數(shù)百或者數(shù)千臺應(yīng)用服務(wù)器時,MySQL服務(wù)器就需要維持十萬級的連接。這時數(shù)據(jù)庫的壓力就會非常大了。連接池技術(shù)就可以派上用場了,可以大大降低數(shù)據(jù)庫連接數(shù)。

基于swoole的AsyncTask模塊實現(xiàn)的連接池是完美方案,編程簡單,沒有數(shù)據(jù)同步和鎖的問題。甚至可以多個服務(wù)共享連接池。缺點是1, 靈活性不如多線程連接池,無法動態(tài)增減連接。2, 有一次進程間通信的開銷。
node.js/ngx_lua等在多進程的模式下,無法開發(fā)出真正的連接池,除非也像swoole_task這樣來實現(xiàn)


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號