網站首頁 編程語言 正文
Request/Response protocols and RTT
Redis是一個client-server
模式的TCP服務,也被稱為Request/Response
協議的實現。
這意味著通常一個請求的完成是遵循下面兩個步驟:
- Client發送一個操作命令給Server,從TCP的套接字Socket中讀取Server的響應值,通常來說這是一種阻塞的方式
- Server執行操作命令,然后將響應值返回給Client
舉個例子
Client: INCR X Server: 1 Client: INCR X Server: 2 Client: INCR X Server: 3 Client: INCR X Server: 4
Clients和Servers是通過網絡進行連接。這就意味著網絡連接可能會很快(比如回環網絡,即本機網絡),也可能很慢(比如兩個主機之間存在多跳網絡)。不管網絡怎么樣,一個數據包從Client到Server,然后相應值又從Server返回Client都需要一定的時間。
這個時間被稱為RTT(Round Trip Time)。當一個Client需要執行多個連續請求(比如添加許多個元素到一個list中,或者清掉Redis中許多個鍵值對),那么RTT是怎樣影響到性能的呢?這個也是很方便去計算的。比如如果RTT的時間為250ms(假設互聯網連接速度非常慢),即使Server可以每秒處理100k個請求,那么最多也只能接受每秒4個請求。
如果是回環網絡,RTT將會特別的短(比如作者的127.0.0.1,RTT的響應時間為44ms),但是對于執行連續多次寫操作時,也是一筆不小的消耗。
其實我們有其他辦法來降低這種場景的消耗,開心不?驚喜不?
Redis Pipelining
在一個Request/Response
方式的服務中有一個特性:即使Client沒有收到之前的響應值,也可以繼續發送新的請求。這種特性意味著我們可以不需要等待Server的響應,可以率先發送許多操作命令給Server,然后在一次性讀取Server的所有響應值。
這種方式被稱為Pipelining
技術,該技術近幾十年來被廣泛的使用。比如多POP3協議的實現就支持這個特性,大大的提升了從server端下載新的郵件的速度。
Redis在很早的時候就支持該項技術,所以不管你運行的是什么版本,你都可以使用pipelining
技術,比如這里有一個使用 netcat 工具的:
$ (printf "PING\r\nPING\r\nPING\r\n"; sleep 1) | nc localhost 6379 +PONG +PONG +PONG
現在我們不需要為每一次請求付出RTT的消耗了,而是一次性發送三個操作命令。為了便于直觀的理解,還是拿之前的說明,使用pipelining
技術該的實現順序如下:
Client: INCR X Client: INCR X Client: INCR X Client: INCR X Server: 1 Server: 2 Server: 3 Server: 4
劃重點(敲黑板):當client使用pipelining
發送操作命令時,server端將強制使用內存來排列響應結果。所以在使用pipelining
發送大量的操作命令的時候,最好確定一個合理的命令條數,一批一批的發送給Server端,比如發送10k個操作命令,讀取響應結果,再發送10k個操作命令,以此類推…雖然說耗時近乎相同,但是額外的內存消耗將是這10k操作命令的排列響應結果所需的最大值。(為防止內存耗盡,選擇一個合理的值)
It’s not just a matter of RTT
Pipelining
不是減少因為 RTT 造成消耗的唯一方式,但是它確實幫助你極大的提升每秒的執行命令數量。事實的真相是:從訪問相應的數據結構并且生成答復結果的角度來看,不使用pipelining
確實代價很低;但是從套接字socket I/O的角度來看,恰恰相反。因為這涉及到了read()
和write()
調用,需要從用戶態切換到內核態。這種上下文切換會特別損耗時間的。
一旦使用了pipelining
技術,很多操作命令將會從同一個read()
調用中執行讀操作,大量的答復結果將會被分發到同一個write()
調用中執行寫操作。基于此,隨著管道的長度增加,每秒執行的查詢數量最開始幾乎呈直線型增加,直到不使用pipelining
技術的基準的10倍,如下圖:?
Some real world code example
不翻譯,基本上就是說使用了pipelining
提升了5倍性能。
Pipelining VS Scripting
Redis Scripting
(2.6+版本可用),通過使用在Server端完成大量工作的腳本Scripting
,可以更加高效的解決大量pipelining
用例。使用腳本Scripting
的最大好處就是在讀和寫的時候消耗更少的性能,使得像讀、寫、計算這樣的操作更加快速。(當client需要寫操作之前獲取讀操作的響應結果時,pepelining
就顯得相形見拙。) 有時候,應用可能需要在使用pipelining
時,發送 EVAL
或者 EVALSHA
命令,這是可行的,并且Redis明確支持這么這種SCRIPT LOAD
命令。(它保證可可以調用 EVALSHA
而不會有失敗的風險)。
Appendix: Why are busy loops slow even on the loopback interface?
讀完全文,你可能還會感到疑問:為什么如下的Redis測試基準 benchmark
會執行這么慢,甚至在Client和Server在一個物理機上也是如此:
FOR-ONE-SECOND: Redis.SET("foo","bar") END
畢竟Redis進程和測試基準benchmark
在相同的機器上運行,并且這是沒有任何實際的延遲和真實的網絡參與,不就是消息通過內存從一個地方拷貝到另一個地方么? 原因是進程在操作系統中并不是一直運行。真實的情景是系統內核調度,調度到進程運行,它才會運行。比如測試基準benchmark
被允許運行,從Redis Server中讀取響應內容(與最后一次執行的命令相關),并且寫了一個新的命令。這時命令將在回環網絡的套接字中,但是為了被Redis Server讀取,系統內核需要調度Redis Server進程(當前正在系統中掛起),周而復始。所以由于系統內核調度的機制,就算是在回環網絡中,仍然會涉及到網絡延遲。 簡言之,在網絡服務器中衡量性能時,使用回環網絡測試并不是一個明智的方式。應該避免使用此種方式來測試基準。
思考
換一種測試方式
參考
- redis.io/topics/pipe…
原文鏈接:https://juejin.cn/post/7087894212909203463
相關推薦
- 2022-03-29 關于Android冷啟動耗時優化詳解_Android
- 2022-07-10 網絡I/o編程模型23 netty的出站與入站中handler加載與執行順序
- 2022-12-14 Android?liveData與viewBinding使用教程_Android
- 2023-05-16 Android中的HOOK技術是什么_Android
- 2023-04-26 C語言二維數組指針的概念及使用_C 語言
- 2022-05-28 教你C#將CSV轉為Excel的實現方法_C#教程
- 2022-11-14 Python?prettytable模塊應用詳解_python
- 2022-07-01 python神經網絡Keras實現GRU及其參數量_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支