国产一本一道久久香蕉下载,色偷偷噜噜噜亚洲男人,日日摸夜添夜夜夜添高潮,在线观看av无需播放器

首頁>新聞動(dòng)態(tài)>尚途學(xué)院

PHP程序如何通過協(xié)程來實(shí)現(xiàn)mysql查詢異步化

來源:http://jbbow.cn/ 作者:admin 瀏覽次數(shù):2738次 發(fā)布時(shí)間:2016-03-07 09:22:40 收藏:添加收藏



寫在文章之初


最近看了很多PHP大牛們的視頻,了解到facebook的mysql查詢可以進(jìn)行異步化,從而提高性能。由于facebook實(shí)現(xiàn)的比較早,他們不得不對php進(jìn)行hack才得以實(shí)現(xiàn)?,F(xiàn)在的php7.0,已經(jīng)無需hack就可以實(shí)現(xiàn)了。


對于一個(gè)web網(wǎng)站的性能來說,瓶頸多半是來自于數(shù)據(jù)庫。一般數(shù)據(jù)庫查詢會(huì)在某個(gè)請求的整體耗時(shí)中占很大比例。如果能提高數(shù)據(jù)庫查詢的效率,網(wǎng)站的整體響應(yīng)時(shí)間會(huì)有很大的下降。如果能實(shí)現(xiàn)mysql查詢的異步化,就可以實(shí)現(xiàn)多條sql語句同時(shí)執(zhí)行。這樣就可以大大縮短mysql查詢的耗時(shí)。


so問題來了,php程序異步執(zhí)行為什么比同步塊?


與異步查詢相反的時(shí)同步查詢。通常情況下mysql的query查詢都是同步方式。下面我們對兩種方式做下對比。對比的例子是,請求兩次select sleep(1)。這條語句在mysql服務(wù)器端大概耗時(shí)1000ms。


下面是程序同步方式的執(zhí)行流程圖:


程序同步執(zhí)行流程圖

上面的流程圖,解釋一下為:

第一步,向mysql服務(wù)器端發(fā)送第一次查詢請求。大概耗時(shí) 1ms

第二步,mysql服務(wù)器端返回第一次查詢的結(jié)果。大概耗時(shí) 1000ms

第三步,向mysql服務(wù)器再次發(fā)送請求。大概耗時(shí) 1ms

第四步,mysql服務(wù)器端返回第二次查詢的結(jié)果。大概耗時(shí) 1000ms

同步的方式執(zhí)行兩次select sleep(1),大概耗時(shí) 2002ms。


而異步方式的執(zhí)行流程圖如下:


程序異步方式執(zhí)行流程圖



我們隊(duì)異步方式執(zhí)行進(jìn)行解釋:

第一步,向mysql服務(wù)器端發(fā)送第一次查詢請求。大概耗時(shí)1ms

第二步,在等待第一次請求返回?cái)?shù)據(jù)的同時(shí),向服務(wù)器端發(fā)送第二次查詢請求。大概耗時(shí) 1ms

第三步,接受mysql服務(wù)器端返回的兩次查詢請求。大概耗時(shí) 1000ms。


對兩次分析對比,我們可以得出:


異步查詢比同步查詢速度快,是因?yàn)槎鄺l查詢語句在服務(wù)器端同時(shí)執(zhí)行,大大縮短了服務(wù)器端的響應(yīng)時(shí)間。并行一般情況下總比串行快嘛。sql語句執(zhí)行時(shí)間越長,效果越明顯。


so,問題又來了,我們該如何實(shí)現(xiàn)mysql的異步查詢?

要實(shí)現(xiàn)異步查詢的關(guān)鍵是能把發(fā)送請求和接受返回?cái)?shù)據(jù)分開。正好mysqlnd中提供了這個(gè)特性。

在mysqlnd中對應(yīng)的方法是:

mysqlnd_async_query 發(fā)送查詢請求

mysqlnd_reap_async_query 獲取查詢結(jié)果

mysqli擴(kuò)展針對mysqlnd的這個(gè)特性做了封裝,在調(diào)用query方法時(shí),傳入MYSQLI_ASYNC即可。


那么,為什么要使用協(xié)程?


查看了我們推薦文章的代碼實(shí)現(xiàn),是不是感覺寫法和平時(shí)不一樣?一般在項(xiàng)目當(dāng)中,我們都是以function的形式去相互調(diào)用,function中包含了數(shù)據(jù)庫查詢。為了保持這個(gè)習(xí)慣,方便大家使用,因此引入了協(xié)程。在php5.5中正好提供了yield和generator,方便我們實(shí)現(xiàn)協(xié)程。示例代碼如下:


<?php
function f1(){
    $db = new db();
    $obj = $db->async_query('select sleep(1)');
    echo "f1 async_query \n";
    yield $obj;
    $row = $db->fetch();
    echo "f1 fetch\n";
    yield $row;
}

function f2(){
    $db = new db();
    $obj = $db->async_query('select sleep(1)');
    echo "f2 async_query\n";
    yield $obj;
    $row = $db->fetch();
    echo "f2 fetch\n";
    yield $row;
}
 
$gen1 = f1();
$gen2 = f2();
 
$gen1->current();
$gen2->current();
$gen1->next();
$gen2->next();
 
$ret1 = $gen1->current();
$ret2 = $gen2->current();
 
var_dump($ret1);
var_dump($ret2);
 
class db{
    static $links;
    private $obj;
    function getConn(){
        $host = '127.0.0.1';
        $user = 'demo';
        $password = 'demo';
        $database = 'demo';
        $this->obj = new mysqli($host, $user, $password, $database);
        self::$links[spl_object_hash($this->obj)] = $this->obj;
        return self::$links[spl_object_hash($this->obj)];
    }
    function async_query($sql){
        $link = $this->getConn();
        $link->query($sql, MYSQLI_ASYNC);
        return $link;
    }
    function fetch(){
        for($i = 1; $i <= 5; $i++){
            $read = $errors = $reject = self::$links;
            $re = mysqli_poll($read, $errors, $reject, 1);
            foreach($read as $obj){
                if($this->obj === $obj){
                    $sql_result = $obj->reap_async_query();
                    $sql_result_array = $sql_result->fetch_array(MYSQLI_ASSOC);//只有一行
                    $sql_result->free();
                    return $sql_result_array;
                }
            }
        }
    }
}
?>


在終端命令行方式執(zhí)行結(jié)果如下:


$time php ./async.php
f1 async_query
f2 async_query
f1 fetch
f2 fetch
array(1) {
    ["sleep(1)"]=>
    string(1) "0"
}
array(1) {
    ["sleep(1)"]=>
    string(1) "0"
}
real    0m1.016s
user    0m0.007s


從結(jié)果上我們可以看出執(zhí)行流程是,先發(fā)了兩次mysql查詢,然后在接受數(shù)據(jù)庫的返回?cái)?shù)據(jù)。正常情況下,至少需要2000ms才能執(zhí)行完畢。但是,real 0m1.016s,說明兩次查詢的耗時(shí)只有1016ms。

tips:以上代碼只是示例代碼,還有一些需要完善的地方。


要注意的地方:


需要注意的是,如果mysql服務(wù)器本身負(fù)載很大,這種并行執(zhí)行的方式就不一定是好的解決方法。因?yàn)椋琺ysql服務(wù)端會(huì)為每個(gè)鏈接創(chuàng)建一個(gè)單獨(dú)的線程進(jìn)行處理。如果創(chuàng)建的線程數(shù)過多,會(huì)給系統(tǒng)造成負(fù)擔(dān)。因此,最好的解決需求的方式是因地制宜,根據(jù)現(xiàn)實(shí)情況制定出最有利當(dāng)前狀況的方案才是上佳之選,不一定某一種模式更好于另一種模式。本文向大家提供的是一種選擇。


網(wǎng)站建設(shè)首選石家莊尚途網(wǎng)絡(luò)科技有限公司,更多網(wǎng)站優(yōu)化,網(wǎng)站建設(shè)信息請關(guān)注:尚途科技,網(wǎng)址:http://jbbow.cn