Redis(七)Redis的噩夢:阻塞


  為什么說阻塞是Redis的噩夢:

  Redis是典型的單線程架構,所有的讀寫操作都是在一條主線程中完成的。當Redis用於高並發場景時,這條線程就變成了它的生命線。如果出現阻塞,哪怕是很短時間,對於應用來說都是噩夢。

  導致阻塞問題的原因:

  • 內在原因:不合理地使用API或數據結構、CPU飽和、持久化阻塞等
  • 外在原因:CPU競爭、內存交換、網絡問題等

  一、發現阻塞

  • 應用方加入異常監控,如日志系統,比如Java語言中的logback或log4j
  • Redis監控系統,如CacheCloud

  二、內在原因

  1.API或數據結構使用不合理

  通常Redis執行命令速度非常快,但是,如果對一個包含上萬個元素的hash結構執行hgetall操作,由於數據量比較大且命令算法復雜度是O(n),這條命令執行速度必然很慢。

  對於高並發的場景應該盡量避免在大對象上執行算法復雜度超過O(n)的命令。

  (1)如何發現慢查詢

  Redis原生提供慢查詢統計功能,執行slowlog get{n}命令可以獲取最近的n條慢查詢命令,默認對於執行超過10毫秒的命令都會記錄到一個定長隊列中,線上實例建議設置為1毫秒便於及時發現毫秒級以上的命令。

  (2)發現慢查詢后如何調整

  • 修改為低算法復雜度的命令
  • 調整大對象:縮減大對象數據或把大對象拆分為多個小對象,防止一次命令操作過多的數據。大對象拆分過程需要視具體的業務決定,如用戶好友集合存儲在Redis中,有些熱點用戶會關注大量好友,這時可以按時間或其他維度拆分到多個集合中。

  (3)如何發現大對象

bigjun@myubuntu:~$ redis-cli -h 192.168.131.130 -p 6379 --bigkeys

# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).

[00.00%] Biggest string found so far 'XML' with 180 bytes
[00.00%] Biggest list   found so far 'hot:user:list' with 5 items

-------- summary -------

Sampled 12 keys in the keyspace!
Total key length in bytes is 108 (avg len 9.00)

Biggest string found 'XML' has 180 bytes
Biggest   list found 'hot:user:list' has 5 items

11 strings with 480 bytes (91.67% of keys, avg size 43.64)
1 lists with 5 items (08.33% of keys, avg size 5.00)
0 sets with 0 members (00.00% of keys, avg size 0.00)
0 hashs with 0 fields (00.00% of keys, avg size 0.00)
0 zsets with 0 members (00.00% of keys, avg size 0.00)
0 streams with 0 entries (00.00% of keys, avg size 0.00)

  2.CPU飽和

  單線程的Redis處理命令時只能使用一個CPU。而CPU飽和是指Redis把單核CPU使用率跑到接近100%。使用top命令很容易識別出對應Redis進程的CPU使用率。CPU飽和是非常危險的,將導致Redis無法處理更多的命令,嚴重影響吞吐量和應用方的穩定性。對於這種情況,首先判斷當前Redis的並發量是否達到極限,建議使用統計命令redis-cli -h {ip} -p {port} --stat獲取當前Redis使用情況

bigjun@myubuntu:~$ redis-cli -h 192.168.131.130 -p 6379 --stat
------- data ------ --------------------- load -------------------- - child -
keys       mem      clients blocked requests            connections          
12         851.05K  2       0       506 (+0)            15          
12         851.05K  2       0       507 (+1)            15          
12         851.05K  2       0       508 (+1)            15          
12         851.05K  2       0       509 (+1)            15          
12         851.05K  2       0       510 (+1)            15          
12         851.05K  2       0       511 (+1)            15          
12         851.05K  2       0       512 (+1)            15          
12         851.05K  2       0       513 (+1)            15          
12         851.05K  2       0       514 (+1)            15   
...

  然后根據info commandstats統計信息分析出命令不合理開銷時間:

192.168.131.130:6379> info commandstats
# Commandstats
cmdstat_type:calls=12,usec=3,usec_per_call=0.25
cmdstat_scan:calls=2,usec=17,usec_per_call=8.50
cmdstat_monitor:calls=1,usec=1,usec_per_call=1.00
cmdstat_del:calls=1,usec=70,usec_per_call=70.00
cmdstat_mset:calls=1,usec=34,usec_per_call=34.00
cmdstat_replconf:calls=398,usec=985,usec_per_call=2.47
cmdstat_llen:calls=1,usec=0,usec_per_call=0.00
cmdstat_script:calls=7,usec=809,usec_per_call=115.57
cmdstat_incr:calls=5,usec=33,usec_per_call=6.60
cmdstat_rpush:calls=1,usec=41,usec_per_call=41.00
cmdstat_dbsize:calls=1,usec=1,usec_per_call=1.00
cmdstat_client:calls=12,usec=189,usec_per_call=15.75
cmdstat_bgsave:calls=1,usec=296,usec_per_call=296.00
cmdstat_get:calls=9,usec=95,usec_per_call=10.56
cmdstat_eval:calls=11,usec=90262859,usec_per_call=8205714.00
cmdstat_lrange:calls=3,usec=114,usec_per_call=38.00
cmdstat_set:calls=3,usec=63,usec_per_call=21.00
cmdstat_keys:calls=10,usec=409,usec_per_call=40.90
cmdstat_strlen:calls=11,usec=15,usec_per_call=1.36
cmdstat_save:calls=1,usec=1410,usec_per_call=1410.00
cmdstat_evalsha:calls=1,usec=20,usec_per_call=20.00
cmdstat_info:calls=14,usec=1005,usec_per_call=71.79
cmdstat_ping:calls=4,usec=5,usec_per_call=1.25
cmdstat_command:calls=2,usec=560,usec_per_call=280.00
cmdstat_mget:calls=2,usec=18,usec_per_call=9.00
cmdstat_psync:calls=1,usec=1608,usec_per_call=1608.00

  3.持久化阻塞

  對於開啟了持久化功能的Redis節點,需要排查是否是持久化導致的阻塞。

  • fork阻塞:ork操作發生在RDB和AOF重寫時,Redis主線程調用fork操作產生共享內存的子進程,由子進程完成持久化文件重寫工作。如果fork操作本身耗時過長,必然會導致主線程的阻塞。
  • AOF刷盤阻塞:當我們開啟AOF持久化功能時,文件刷盤的方式一般采用每秒一次,后台線程每秒對AOF文件做fsync操作。當硬盤壓力過大時,fsync操作需要等待,直到寫入完成。如果主線程發現距離上一次的fsync成功超過2秒,為了數據安全性它會阻塞直到后台線程執行fsync操作完成。這種阻塞行為主要是硬盤壓力引起。
  • HugePage寫操作阻塞:子進程在執行重寫期間利用Linux寫時復制技術降低內存開銷,因此只有寫操作時Redis才復制要修改的內存頁。對於開啟Transparent HugePages的操作系統,每次寫命令引起的復制內存頁單位由4K變為2MB,放大了512倍,會拖慢寫操作的執行時間,導致大量寫操作慢查詢。

  三、外在原因

  1.CPU競爭

  • 進程競爭:Redis是典型的CPU密集型應用,不建議和其他多核CPU密集型服務部署在一起。當其他進程過度消耗CPU時,將嚴重影響Redis吞吐量。可以通過top、sar等命令定位到CPU消耗的時間點和具體進程,這個問題比較容易發現,需要調整服務之間部署結構。
  • 綁定CPU:部署Redis時為了充分利用多核CPU,通常一台機器部署多個實例。常見的一種優化是把Redis進程綁定到CPU上,用於降低CPU頻繁上下文切換的開銷。這個優化技巧正常情況下沒有問題,但是存在例外情況,當Redis父進程創建子進程進行RDB/AOF重寫時,如果做了CPU綁定,會與父進程共享使用一個CPU。子進程重寫時對單核CPU使用率通常在90%以上,父進程與子進程將產生激烈CPU競爭,極大影響Redis穩定性。因此對於開啟了持久化或參與復制的主節點不建議綁定CPU。

  2.內存交換

  內存交換(swap)對於Redis來說是非常致命的,Redis保證高性能的一個重要前提是所有的數據在內存中。如果操作系統把Redis使用的部分內存換出到硬盤,由於內存與硬盤讀寫速度差幾個數量級,會導致發生交換后的Redis性能急劇下降。

  (1)識別內存交換:

bigjun@myubuntu:~$ redis-cli -h 192.168.131.130 -p 6379 info server | grep process_id
process_id:2668

root@myubuntu:~# cat /proc/2668/smaps  | grep Swap
Swap:                  0 kB
SwapPss:               0 kB
Swap:                  0 kB
SwapPss:               0 kB
Swap:                  0 kB
SwapPss:               0 kB
Swap:                  0 kB
SwapPss:               0 kB
...

  (2)預防內存交換:

  • 保證機器充足的可用內存。
  • 確保所有Redis實例設置最大可用內存(maxmemory),防止極端情況下Redis內存不可控的增長。
  • 降低系統使用swap優先級。

  3.網絡問題

  (1)連接拒絕

  • 網絡閃斷(網絡割接或者帶寬耗盡)
  • Redis連接拒絕(超過客戶端最大連接數)
  • 連接溢出(進程限制或backlog隊列溢出)

  (2)網絡延遲

  網絡延遲取決於客戶端到Redis服務器之間的網絡環境。主要包括它們之間的物理拓撲和帶寬占用情況。常見的物理拓撲按網絡延遲由快到慢可分為:同物理機>同機架>跨機架>同機房>同城機房>異地機房。但它們容災性正好相反,同物理機容災性最低而異地機房容災性最高。

  網絡延遲問題經常出現在跨機房的部署結構上,對於機房之間延遲比較嚴重的場景需要調整拓撲結構,如把客戶端和Redis部署在同機房或同城機房等。
  帶寬瓶頸通常出現在以下幾個方面:

  • 機器網卡帶寬。
  • 機架交換機帶寬。
  • 機房之間專線帶寬。

  (3)網卡軟中斷

  網卡軟中斷是指由於單個網卡隊列只能使用一個CPU,高並發下網卡數據交互都集中在同一個CPU,導致無法充分利用多核CPU的情況。網卡軟中斷瓶頸一般出現在網絡高流量吞吐的場景。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM