背景
由於對redis緩存中數據有批量操作,例如預熱緩存數據,或者在列表頁批量去獲取緩存數據,在使用了multi批量提交事務后,發現redis壓力高居不下,而使用了pipeline之后壓力回落了平常,也因為這個案例,特在此寫個分析與筆記。
multi
簡介
標記一個事務塊的開始。 事務塊內的多條命令會按照先后順序被放進一個隊列當中,最后由 EXEC 命令原子性(atomic)地執行。
實現原理
我用php擴展調起redis服務,執行,代碼如下:
$redis = new redis(); $redis->connect('127.0.0.1',6379); $handle = $redis->multi(); $handle->incr('a'); $handle->incr('b'); $handle->exec(); 復制代碼
為了查看這期間具體的連接過程,用wireshark監聽回環地址端口6379,抓包請求如下圖所示:

redis客戶端與服務端建立連接后,multi標記事務開始,之后每次執行,服務端返回queued隊列標志。查看redis源碼src/multi.c文件:
void queueMultiCommand(client *c) {
multiCmd *mc;
int j;
c->mstate.commands = zrealloc(c->mstate.commands,
sizeof(multiCmd)*(c->mstate.count+1));
mc = c->mstate.commands+c->mstate.count;
mc->cmd = c->cmd;
mc->argc = c->argc;
mc->argv = zmalloc(sizeof(robj*)*c->argc);
memcpy(mc->argv,c->argv,sizeof(robj*)*c->argc);
for (j = 0; j < c->argc; j++) incrRefCount(mc->argv[j]); c->mstate.count++; } 復制代碼
在上述源碼中可以看到redis服務端每次會把事務塊中的命令保存到內存中,上述簡介已經解釋過最后通過exec命令執行,再看下面示例圖的返回結果可以了解到redis服務端一次性返回所有命令執行返回結果。

pipeline
簡介
客戶端將執行的命令寫入到緩沖中,最后由exec命令一次性發送給redis執行返回。
實現原理
同樣,用相關代碼調用redis抓包;
$redis = new redis(); $redis->connect('127.0.0.1',6379); $handle = $redis->pipeline(); $handle->incr('a'); $handle->incr('b'); $handle->exec(); 復制代碼
繼續用wireshark抓包,如下圖所示
- pipeline 客戶端請求包示例圖

- pipeline 服務端返回包示例圖

這上面的圖片簡要分析一下,pipeline管道操作是需要客戶端與服務端的支持,客戶端將命令寫入緩沖,最后再通過exec命令發送給服務端,服務端通過命令拆分,逐個執行返回結果。
兩者的區別
由上面的請求也可以看出了兩者最明顯的區別是客戶端發送請求的方式不一樣,具體相關區別如下:
- pipeline選擇客戶端緩沖,multi選擇服務端緩沖;
- 請求次數的不一致,multi需要每個命令都發送一次給服務端,pipeline最后一次性發送給服務端,請求次數相對於multi減少
- multi/exec可以保證原子性,而pipeline不保證原子性
作者:Walker
鏈接:https://juejin.im/post/5b42e025f265da0fa332d4dc
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。