redis管道pipeline的運用

Redis使用的是客戶端-服務器(CS)模型和請求/響應協議的TCP服務器。這意味着一般狀況下一個請求會遵循如下步驟:redis

  • 客戶端向服務端發送一個查詢請求,並監聽Socket返回,一般是以阻塞模式,等待服務端響應。
  • 服務端處理命令,並將結果返回給客戶端。

普通模式與管道模式

分析
  • 普通模式:因爲通訊會有網絡延遲,假如client和server之間的包傳輸時間須要0.125秒。那麼上面的三個命令6個報文至少須要0.75秒才能完成。這樣即便redis每秒能處理100個命令,而咱們的client也只能一秒鐘發出四個命令。這顯然沒有充分利用 redis的處理能力。數組

  • 管道模式:(pipeline)能夠一次性發送多條命令並在執行完後一次性將結果返回,pipeline經過減小客戶端與redis的通訊次數來實現下降往返延時時間,並且Pipeline 實現的原理是隊列,而隊列的原理是時先進先出,這樣就保證數據的順序性。 Pipeline 的默認的同步的個數爲53個,也就是說arges中累加到53條數據時會把數據提交。其過程以下圖所示:client能夠將三個命令放到一個tcp報文一塊兒發送,server則能夠將三條命令的處理結果放到一個tcp報文返回。
      須要注意到是用 pipeline方式打包命令發送,redis必須在處理完全部命令前先緩存起全部命令的處理結果。打包的命令越多,緩存消耗內存也越多。因此並非打包的命令越多越好。具體多少合適須要根據具體狀況測試。
      
    Redis普通模式與管道模式

案例一:將100萬條數據寫入redis

//產生100萬條數據到指定文件
declare(strict_types=1);//開啓強類型模式

function random($length, $numeric = false)
{
    $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
    $seed = $numeric ? (str_replace('0', '', $seed) . '012340567890') : ($seed . 'zZ' . strtoupper($seed));
    if ($numeric) {
        $hash = '';
    } else {
        $hash = chr(rand(1, 26) + rand(0, 1) * 32 + 64);
        $length--;
    }
    $max = strlen($seed) - 1;
    for ($i = 0; $i < $length; $i++) {
        $hash .= $seed{mt_rand(0, $max)};
    }
    return $hash;
}

$filePath = './data.txt';
for ($i = 0; $i <= 1000000; $i++) {
    $str = random(10, true);
    file_put_contents($filePath, $str . PHP_EOL, FILE_APPEND);
}
//讀取數據經過管道方式寫入到redis
$lines = file_get_contents($filePath);//獲取文件內容
ini_set('memory_limit', '-1');//不要限制Mem大小,不然會報錯

$arr = explode(PHP_EOL, $lines);//轉換成數組

//echo $arr['1000000'] ?? 'null';

try {
    $redis = new \Redis();
    $redis->connect('192.168.1.9', 6379);
    $redis->auth('*****');//密碼驗證
    $redis->select(0);//選擇庫
    $redis->pipeline();//開啓管道

    foreach ($arr as $key => $value) {
        $redis->hsetNx('helloworld', (string)$key, $value);
    }
    $redis->exec();

    echo $redis->hGet('helloworld', '1000000') . PHP_EOL;
    echo $redis->hGet('helloworld', '1000001') . PHP_EOL;
} catch (\Exception $e) {
    echo $e->getMessage();
}


案例二:經過管道批量設置與讀取

//批量設置
try {
    $redis = new \Redis();
    $redis->connect('192.168.1.9', 6379);
    $redis->auth('******');
    $redis->select(0);
    $redis->pipeline();//開啓管道

    $redis->set('str1', 'h');
    $redis->set('str2', 'e');
    $redis->set('str3', 'l');
    $redis->set('str4', 'l');
    $redis->set('str5', 'o');
    $redis->set('str6', 'w');
    $redis->set('str7', 'o');
    $redis->set('str8', 'r');
    $redis->set('str9', 'l');
    $redis->set('str10', 'd');
    $result = $redis->exec();
    print_r($result);
} catch (\Exception $e) {
    echo $e->getMessage();
}

結果:
Array
(
    [0] => 1
    [1] => 1
    [2] => 1
    [3] => 1
    [4] => 1
    [5] => 1
    [6] => 1
    [7] => 1
    [8] => 1
    [9] => 1
)
//批量讀取
try {
    $redis = new \Redis();
    $redis->connect('192.168.1.9', 6379);
    $redis->auth('******');
    $redis->select(0);
    $redis->pipeline();//開啓管道

    $redis->get('str1');
    $redis->get('str2');
    $redis->get('str3');
    $redis->get('str4');
    $redis->get('str5');
    $redis->get('str6');
    $redis->get('str7');
    $redis->get('str8');
    $redis->get('str9');
    $redis->get('str10');
    $result = $redis->exec();
    print_r($result);
} catch (\Exception $e) {
    echo $e->getMessage();
}

結果:
Array
(
    [0] => h
    [1] => e
    [2] => l
    [3] => l
    [4] => o
    [5] => w
    [6] => o
    [7] => r
    [8] => l
    [9] => d
)
相關文章
相關標籤/搜索