Redis批量操作详解及性能分析
通過mget批量執行指令可以節約網絡連接和數據傳輸開銷,在高并發場景下可以節約大量系統資源。本文中,我們更進一步,比較一下redis提供的幾種批量執行指令的性能。
1.?為什么需要批量執行redis指令
眾所周知,Redis協議采取的是客戶端-服務器方式,即在一次round trip中,客戶端發送一條指令,服務端解析指令并執行,然后向客戶端返回結果。這是一種典型的tcp交互方式。
粗略的分,客戶端發起一次Redis請求主要有如下開銷:
- socket IO導致的上下文切換開銷 (嚴重讀寫系統開銷)
熟悉OS/Linux的童鞋都知道,一次redis請求在客戶端和服務端分別至少會存在一次read()和一次write(),作為系統調用,read/write的成本高于普通的函數調用,因此,在單個命令重復調用場景下,大量的read/write系統調用會產生明顯的系統開銷。
- 指令執行開銷 (輕微指令開銷)
Redis采用C實現,使用了輕量級的hash表、skipList跳表等數據結構實現了高效的緩存。因此,單條執行大多數指令的成本非常低。因此,相對而言,IO的開銷顯得更加無法忽略。
- (高并發下)資源競爭和系統調度調度開銷 (Redis競爭抖動)
客戶端的影響非常明顯。在高壓力下,如果采用循環(loop)方式調用多次指令來完成某個服務請求,那么在高并發下,多個請求會在多個線程中同時競爭redis連接資源多次,導致連接池壓力增加,線程上下文切換更加頻發,最終會導致請求RTT(round-trip time)急劇惡化。如果每個請求只搶占一次redis連接并通過批量執行的方式一次處理多個請求,則單次請求的RTT會有顯著提升。
在服務端,因為我們通常將redis綁定到CPU(不管是通過物理機還是通過docker),因此一般而言不存在系統調度/資源競爭的開銷。但是由于redis對QPS敏感,如果因為客戶端使用不合理而造成QPS放大效應,則redis可能更早觸及性能瓶頸而導致系統響應嚴重下降。
筆者曾經在一次性能調優中發現,每次服務請求訪問redis次數高達數十次,使得redis請求次數達到服務QPS的數十倍,觸發了redis服務器的極限(大概5~10萬QPS)而導致服務性能低下,多個請求對redis連接池進行了激烈競爭,并且由于redis響應速度的下降導致大量線程在獲取連接處阻塞并頻繁進行線程切換。在改進實現采用了批量指令處理后,服務性能瞬間達到了數十倍的提升。因此,如果每次服務掉用需要觸發多次redis請求,合理地適用批量執行技術,可以使系統運行更加有效,數據吞吐得到明顯提升。
2.?redis批量指令介紹
2.1. 批量命令即redis對應的命令
- mget(適用于string類型)
- mset(適用于string類型)
- hmget(適用于hash類型)
- hmset(適用于hash類型)
嚴格來說上述命令不屬于批量操作,而是在一個指令中處理多個key。
- 優勢:性能優異,因為是單條指令操作,因此性能略優于其他批量操作指令
- 劣勢:批量命令不保證原子性,存在部分成功部分失敗的情況,需要應用程序解析返回的結果并做相應處理???批量命令在key數目巨大時存在RRT與key數目成比例放大的性能衰減,會導致單實例響應性能(RRT)嚴重下降
2.2 管道 pipeline
管道(pipelining)方式意味著客戶端可以在一次請求中發送多個命令。
- 優勢:
- 劣勢:
2.3 事務操作
事務(Transactions)操作允許在一步中執行一組redis操作,并對這一組redis命令有如下保證:
單個事務的所有命令,或者被全部執行,或者一個也不會被執行,因此事務保證了redis操作的原子性。命令EXEC觸發事務中所有命令的執行,因此如果一個client在事務上下文中丟失了連接,那么不會有任何一條命令被執行;相反如果client已經調用了EXEC,那么所有命令都會被執行。
當使用append-only文件時,Redis保證僅使用一個write(2)系統調用來將事務結果寫入磁盤。然而如果Redis server崩潰或者被系統管理員使用hard方式kill了進程,那么還是有可能只寫入了部分操作。Redis在重啟時可以檢測到這一問題,并以error退出。這時,可以使用redis-check-aof工具來對append-only文件進行修復,它將會刪除部分寫入的事務這樣server就可以啟動了。
- 優勢:
- 劣勢:
事務的所有命令會分批發送給redis實例,redis返回+QUEUED,表示命令已入列,但是不會執行任何命令。在收到EXEC命令時,一次執行本事務的所有命令。因此事務的性能略低于pipeline,但是相差不多。
在keys競爭激烈時,WATCH提供的樂觀鎖由于競爭過多而性能低下,應該盡量避免。
2.4?基于管道的事務
在Redis中,管道是通過RESP,即redis協議來實現的,它允許在一個消息包中按照指定格式傳遞多個命令。而事務是通過命令實現的,因此管道和事務之間并不沖突,事務可以承載與管道之上。在某些場景,需要在一次請求處理中發起多次事務的場景下,通過引入管道,可以獲得略高于單獨執行多次事務的性能,但是兩者的差距非常小,小到可以忽略。
3.?壓測用例分析
針對上述4種批量操作,設計如下case:
- 條件:在本地單機redis中創建1,000,000對key-value,key長8字節,value長5字節
- 測試過程:
單位:ms
3.1?SET性能壓測結果
3.2?GET性能壓測結果
3.3 結論
從上述測試結果中可以看出,不同的處理方式,最終性能曲線基本一致。
- mset性能最好,吞吐量最高,因為mset是作為單條命令執行,在命令解析和執行上都更有效率
- pipeline好于transaction in pipeline,因為事務會導致命令入列和出列會稍許浪費cpu時間
- transaction in pipeline微弱領先于transaction,但是幾乎沒有區別,可以理解為pipeline在命令傳輸上更有效率。
- 總得來說,在批量模式下,四種操作都比普通的get/set性能上有幾大的提升。
- 在當前生產環境中使用較多的Redis Cluster環境中,上述四種批量操作的使用場景都比較有限,其中transaction不支持,pipeline建議僅用于單slot且目前支持的客戶端很少,mget/mset也僅僅可以操作于單slot中的key。
總結
以上是生活随笔為你收集整理的Redis批量操作详解及性能分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 哈佛成功金句 -25则
- 下一篇: NDCG:推荐系统/搜索评价指标