1. 程式人生 > >Redis的Pipeline、Multi對比

Redis的Pipeline、Multi對比

port 無法 $1 12c 疑問 服務端 var AR PE

網上對於Redis的Pipeline和Multi的兩種模式的速度對比的文章,都大概只提到了Pipeline比Multi更快,原因是Pipeline是一次性全部發送,一次性全部執行,諸如此類吧啦吧啦。

我的疑問是:依據從哪而來?Pipeline真的就是等所有請求都收到後才一次性執行的嗎?難道Multi就不是一次性執行的了嗎?

其中一篇參考文章:http://blog.fbbin.com/archives/1277

由於本人太忙不想去閱讀Redis Server的源代碼(其實是懶),就通過TCPDump來分析吧。

步驟:

1. 先在Redis Server上啟動TCPDump: tcpdump -i eno16777736 port 6379 -X

eno16777736是我的虛擬機網卡,吃瓜群眾們替換成自己的就行。

2. 測試Multi:

<?php

$redis = new redis();
$result = $redis->connect(‘localhost‘, 6379);

$multi = $redis->multi();

for($i=0;$i<100;$i++)
$multi->set(‘testkey‘ . (string)$i, ‘helloworld‘ . (string)$i);

$replies = $multi->exec();

var_dump($replies);

?>

  

觀察TCPDump數據:

19:04:50.987407 IP slave105.50396 > master104.6379: Flags [P.], seq 1:16, ack 1, win 115, options [nop,nop,TS val 2306103 ecr 29105036], length 15
0x0000: 4500 0043 59ec 4000 4006 5ca7 c0a8 0169 E..CY.@.@.\....i
0x0010: c0a8 0168 c4dc 18eb 6289 69dd 89bf b38e ...h....b.i.....
0x0020: 8018 0073 67c1 0000 0101 080a 0023 3037 ...sg........#07

0x0030: 01bc 1b8c 2a31 0d0a 2435 0d0a 4d55 4c54 ....*1..$5..MULT    //客戶端請求啟動Multi
0x0040: 490d 0a I..

19:04:50.987662 IP master104.6379 > slave105.50396: Flags [P.], seq 1:6, ack 16, win 114, options [nop,nop,TS val 29105037 ecr 2306103], length 5
0x0000: 4500 0039 a803 4000 4006 0e9a c0a8 0168 E..9..@[email protected]
0x0010: c0a8 0169 18eb c4dc 89bf b38e 6289 69ec ...i........b.i.
0x0020: 8018 0072 844d 0000 0101 080a 01bc 1b8d ...r.M..........
0x0030: 0023 3037 2b4f 4b0d 0a .#07+OK..                //服務端回應啟動Multi成功

19:04:50.988418 IP slave105.50396 > master104.6379: Flags [P.], seq 16:61, ack 6, win 115, options [nop,nop,TS val 2306104 ecr 29105037], length 45
0x0000: 4500 0061 59ee 4000 4006 5c87 c0a8 0169 E..aY.@.@.\....i    //客戶端請求Set("testkey0")
0x0010: c0a8 0168 c4dc 18eb 6289 69ec 89bf b393 ...h....b.i.....
0x0020: 8018 0073 4e5a 0000 0101 080a 0023 3038 ...sNZ.......#08
0x0030: 01bc 1b8d 2a33 0d0a 2433 0d0a 5345 540d ....*3..$3..SET.
0x0040: 0a24 380d 0a74 6573 746b 6579 300d 0a24 .$8..testkey0..$
0x0050: 3131 0d0a 6865 6c6c 6f77 6f72 6c64 300d 11..helloworld0.
0x0060: 0a .
19:04:50.988551 IP master104.6379 > slave105.50396: Flags [P.], seq 6:15, ack 61, win 114, options [nop,nop,TS val 29105038 ecr 2306104], length 9
0x0000: 4500 003d a804 4000 4006 0e95 c0a8 0168 E..=..@[email protected]    //服務端返回testkey0已加入隊列。
0x0010: c0a8 0169 18eb c4dc 89bf b393 6289 6a19 ...i........b.j.
0x0020: 8018 0072 8451 0000 0101 080a 01bc 1b8e ...r.Q..........
0x0030: 0023 3038 2b51 5545 5545 440d 0a .#08+QUEUED..
19:04:50.989330 IP slave105.50396 > master104.6379: Flags [P.], seq 61:106, ack 15, win 115, options [nop,nop,TS val 2306105 ecr 29105038], length 45
0x0000: 4500 0061 59ef 4000 4006 5c86 c0a8 0169 E..aY.@.@.\....i      //客戶端請求Set("testkey1")
0x0010: c0a8 0168 c4dc 18eb 6289 6a19 89bf b39c ...h....b.j.....
0x0020: 8018 0073 4c22 0000 0101 080a 0023 3039 ...sL".......#09
0x0030: 01bc 1b8e 2a33 0d0a 2433 0d0a 5345 540d ....*3..$3..SET.
0x0040: 0a24 380d 0a74 6573 746b 6579 310d 0a24 .$8..testkey1..$
0x0050: 3131 0d0a 6865 6c6c 6f77 6f72 6c64 310d 11..helloworld1.
0x0060: 0a .
19:04:50.989817 IP master104.6379 > slave105.50396: Flags [P.], seq 15:24, ack 106, win 114, options [nop,nop,TS val 29105039 ecr 2306105], length 9
0x0000: 4500 003d a805 4000 4006 0e94 c0a8 0168 E..=..@[email protected]    //服務端返回testkey1已加入隊列。
0x0010: c0a8 0169 18eb c4dc 89bf b39c 6289 6a46 ...i........b.jF
0x0020: 8018 0072 8451 0000 0101 080a 01bc 1b8f ...r.Q..........
0x0030: 0023 3039 2b51 5545 5545 440d 0a .#09+QUEUED..
19:04:50.990195 IP slave105.50396 > master104.6379: Flags [P.], seq 106:151, ack 24, win 115, options [nop,nop,TS val 2306106 ecr 29105039], length 45
0x0000: 4500 0061 59f0 4000 4006 5c85 c0a8 0169 E..aY.@.@.\....i    //客戶端請求Set("testkey2")
0x0010: c0a8 0168 c4dc 18eb 6289 6a46 89bf b3a5 ...h....b.jF....
0x0020: 8018 0073 49ea 0000 0101 080a 0023 303a ...sI........#0:
0x0030: 01bc 1b8f 2a33 0d0a 2433 0d0a 5345 540d ....*3..$3..SET.
0x0040: 0a24 380d 0a74 6573 746b 6579 320d 0a24 .$8..testkey2..$
0x0050: 3131 0d0a 6865 6c6c 6f77 6f72 6c64 320d 11..helloworld2.
0x0060: 0a .
19:04:50.990285 IP master104.6379 > slave105.50396: Flags [P.], seq 24:33, ack 151, win 114, options [nop,nop,TS val 29105040 ecr 2306106], length 9
0x0000: 4500 003d a806 4000 4006 0e93 c0a8 0168 E..=..@[email protected]    //服務端返回testkey2已加入隊列。
0x0010: c0a8 0169 18eb c4dc 89bf b3a5 6289 6a73 ...i........b.js
0x0020: 8018 0072 8451 0000 0101 080a 01bc 1b90 ...r.Q..........
0x0030: 0023 303a 2b51 5545 5545 440d 0a .#0:+QUEUED..
19:04:50.990578 IP slave105.50396 > master104.6379: Flags [P.], seq 151:165, ack 33, win 115, options [nop,nop,TS val 2306106 ecr 29105040], length 14
0x0000: 4500 0042 59f1 4000 4006 5ca3 c0a8 0169 E..BY.@.@.\....i
0x0010: c0a8 0168 c4dc 18eb 6289 6a73 89bf b3ae ...h....b.js....
0x0020: 8018 0073 bc17 0000 0101 080a 0023 303a ...s.........#0:
0x0030: 01bc 1b90 2a31 0d0a 2434 0d0a 4558 4543 ....*1..$4..EXEC       //客戶端請求exec,既執行整個multi
0x0040: 0d0a ..
19:04:50.990828 IP master104.6379 > slave105.50396: Flags [P.], seq 33:52, ack 165, win 114, options [nop,nop,TS val 29105040 ecr 2306106], length 19
0x0000: 4500 0047 a807 4000 4006 0e88 c0a8 0168 E..G..@[email protected]
0x0010: c0a8 0169 18eb c4dc 89bf b3ae 6289 6a81 ...i........b.j.
0x0020: 8018 0072 845b 0000 0101 080a 01bc 1b90 ...r.[..........
0x0030: 0023 303a 2a33 0d0a 2b4f 4b0d 0a2b 4f4b .#0:*3..+OK..+OK      //服務端執行整個multi,並一次性返回所有Set()的結果
0x0040: 0d0a 2b4f 4b0d 0a ..+OK..

3. 測試Pipeline:

<?php

$redis = new redis();
$result = $redis->connect(‘master104‘, 6379);

$pipe = $redis->multi(Redis::PIPELINE);

for($i=0;$i<100;$i++)
$pipe->set(‘testkey‘ . (string)$i, ‘helloworld‘ . (string)$i);

$replies = $pipe->exec();

var_dump($replies);

觀察TCPDump數據:

19:13:59.922411 IP slave105.50404 > master104.6379: Flags [.], seq 1:4345, ack 1, win 115, options [nop,nop,TS val 2855039 ecr 29653969], length 4344
0x0000: 4500 112c 48a8 4000 4006 5d02 c0a8 0169 E..,H.@.@.]....i
0x0010: c0a8 0168 c4e4 18eb ebe9 d8dc 3d91 f923 ...h........=..#
0x0020: 8010 0073 9540 0000 0101 080a 002b 907f ...s.@.......+..
0x0030: 01c4 7bd1 2a33 0d0a 2433 0d0a 5345 540d ..{.*3..$3..SET.
0x0040: 0a24 380d 0a74 6573 746b 6579 300d 0a24 .$8..testkey0..$
0x0050: 3131 0d0a 6865 6c6c 6f77 6f72 6c64 300d 11..helloworld0.
0x0060: 0a2a 330d 0a24 330d 0a53 4554 0d0a 2438 .*3..$3..SET..$8
0x0070: 0d0a 7465 7374 6b65 7931 0d0a 2431 310d ..testkey1..$11.
0x0080: 0a68 656c 6c6f 776f 726c 6431 0d0a 2a33 .helloworld1..*3
0x0090: 0d0a 2433 0d0a 5345 540d 0a24 380d 0a74 ..$3..SET..$8..t
0x00a0: 6573 746b 6579 320d 0a24 3131 0d0a 6865 estkey2..$11..he
0x00b0: 6c6c 6f77 6f72 6c64 320d 0a2a 330d 0a24 lloworld2..*3..$
...

(此處省略中間數據包)

...

0x10f0: 0a24 3132 0d0a 6865 6c6c 6f77 6f72 6c64 .$12..helloworld      
0x1100: 3931 0d0a 2a33 0d0a 2433 0d0a 5345 540d 91..*3..$3..SET.
0x1110: 0a24 390d 0a74 6573 746b 6579 3932 0d0a .$9..testkey92..      
0x1120: 2431 320d 0a68 656c 6c6f 776f $12..hellowo              

到testkey92的時候,由於已經達到了Redis應用數據包的大小,所以先只發送到這,接下來看:

19:16:15.521869 IP slave105.50406 > master104.6379: Flags [P.], seq 4345:4681, ack 1, win 115, options [nop,nop,TS val 2990641 ecr 29789570], length 336
0x0000: 4500 0184 3d25 4000 4006 782d c0a8 0169 E...=%@[email protected]
0x0010: c0a8 0168 c4e6 18eb c3fc b008 f41e ad81 ...h............
0x0020: 8018 0073 0f61 0000 0101 080a 002d a231 ...s.a.......-.1
0x0030: 01c6 8d82 726c 6439 320d 0a2a 330d 0a24 ....rld92..*3..$
0x0040: 330d 0a53 4554 0d0a 2439 0d0a 7465 7374 3..SET..$9..test
0x0050: 6b65 7939 330d 0a24 3132 0d0a 6865 6c6c key93..$12..hell

...

0x0160: 0d0a 2439 0d0a 7465 7374 6b65 7939 390d ..$9..testkey99.    //此時全部100個發送完畢
0x0170: 0a24 3132 0d0a 6865 6c6c 6f77 6f72 6c64 .$12..helloworld
0x0180: 3939 0d0a 99..

19:16:15.522107 IP master104.6379 > slave105.50406: Flags [P.], seq 1:461, ack 4681, win 204, options [nop,nop,TS val 29789572 ecr 2990641], length 460
0x0000: 4500 0200 2005 4000 4006 94d1 c0a8 0168 E.....@[email protected]
0x0010: c0a8 0169 18eb c4e6 f41e ad81 c3fc b158 ...i...........X
0x0020: 8018 00cc 8614 0000 0101 080a 01c6 8d84 ................
0x0030: 002d a231 2b4f 4b0d 0a2b 4f4b 0d0a 2b4f .-.1+OK..+OK..+O    //服務端將100個請求全部執行完成後,一次性返回所有結果
0x0040: 4b0d 0a2b 4f4b 0d0a 2b4f 4b0d 0a2b 4f4b K..+OK..+OK..+OK
0x0050: 0d0a 2b4f 4b0d 0a2b 4f4b 0d0a 2b4f 4b0d ..+OK..+OK..+OK.
0x0060: 0a2b 4f4b 0d0a 2b4f 4b0d 0a2b 4f4b 0d0a .+OK..+OK..+OK..

...

19:16:15.522221 IP master104.6379 > slave105.50406: Flags [P.], seq 461:501, ack 4681, win 204, options [nop,nop,TS val 29789572 ecr 2990641], length 40
0x0000: 4500 005c 2006 4000 4006 9674 c0a8 0168 E..\..@[email protected]
0x0010: c0a8 0169 18eb c4e6 f41e af4d c3fc b158 ...i.......M...X
0x0020: 8018 00cc 8470 0000 0101 080a 01c6 8d84 .....p..........
0x0030: 002d a231 2b4f 4b0d 0a2b 4f4b 0d0a 2b4f .-.1+OK..+OK..+O
0x0040: 4b0d 0a2b 4f4b 0d0a 2b4f 4b0d 0a2b 4f4b K..+OK..+OK..+OK
0x0050: 0d0a 2b4f 4b0d 0a2b 4f4b 0d0a ..+OK..+OK..

總結一下:

1. Multi:

  1.1. 每發送一條指令,都需要單獨發給服務器,服務器再單獨返回“該條指令已加入隊列”這個消息。這是比Pipeline慢的原因之一。

  1.2. Multi執行的時候會先暫停其他命令的執行,類似於加了個鎖,直到整個Multi結束完成再繼續其他客戶端的請求。這是Multi能保證一致性的原因,也是比Pipeline慢的原因之二。(需要讀Redis Server的代碼,從TCPDump上看不出)

2. Pipeline:

  2.1. 將所有命令打包一次性發送。發送成功後,服務端不用返回類似“命令已收到”這樣的消息,而是一次性批量執行所有命令,成功後再一次性返回所有處理結果。

  2.2. 服務端處理命令的時候,不需要加鎖,而是與其他客戶端的命令混合在一起處理,所以無法保證一致性。

適用場景:

1. 如果要順序執行一組命令(既網上所謂的“Redis事務”),Multi很合適。

2. 如果要往Redis裏批量插入Log, 或者使用Redis List做為隊列並插入很多消息的話,Pipleline是挺合適的。

Redis的Pipeline、Multi對比