1. 程式人生 > >Redis客戶端查詢緩衝區和輸出緩衝區

Redis客戶端查詢緩衝區和輸出緩衝區

每個Redis客戶端(以下簡稱”Client”)都有多個狀態屬性,而理解和分析這些屬性,對於我們設計Redis鍵空間和運營管理都有幫助。
本文將詳細分析Client的兩個重要屬性:Query buffer(輸入緩衝區)、Output buffers(輸出緩衝區)

Redis Client屬性一覽

使用redis client命令可檢視當前Redis例項的所有客戶端;每行資料對應一個客戶端。

127.0.0.1:6390> client listid=2 addr=127.0.0.1:53184 fd=8 name= age=33 idle=24 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=clientid=3 addr=127.0.0.1:53190 fd=7 name= age=2 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client

以上為兩個客戶端,每個包含18個欄位屬性;其中屬性的基本含義此處簡單說明,後文會對重啟指標深入分析。

1234567891011121314151617 id:客戶端唯一標識, 每新建立一個連線就自增1;redis重啟後重置1。addr: 客戶端源ip:port;用於分析異常的客戶端,定位是由哪個伺服器哪個程序引起的; 如id=2的客戶端 netstat -anp | grep 53184fd: socket的檔案描述符;數值同lsof的FD欄位相同name: 客戶端的名字,預設不會設定,一般用處不大。可手動執行[clientsetname](http://redis.io/commands/client-setname)age: 客戶端存活的秒數idle: 空閒的秒數;用於回收客戶端和分析大量連線時有用flages:客戶端型別的標誌, 共13種,常用的幾種:N(普通客戶端),M(master),S(slave),O(執行monitor)db:客戶端當前使用的database序號sub/psub: 快訂閱的頻道/模式數multi:當前事務中已執行命令個數qbuf: query buffer的位元組數 重要qbuf-free: query buffer的剩餘位元組數obl:定長Output buffer的使用位元組數oll:可變大小output buffer的物件個數omem:可變大小output buffer的記憶體使用位元組數 重要events: 檔案描述符事作件(r/w)cmd:客戶端最近一次執行的命令,不包含引數

Redis Client Query Buffer

每個Client都有一個query buffer(查詢快取區或輸入快取區), 它用於儲存客戶端的傳送命令,redis server從query buffer獲取命令並執行。

query buffer size

每個客戶端query buffer自動動態調整使用記憶體大小的,範圍在0~1GB之間;當某個客戶端的query buffer使用超過1GB, server會立即關閉它,為避免過度使用記憶體,觸發oom killer。
很遺憾query buffer的大小限制是硬編碼的1GB,沒法控制配置引數修改。

123 server.h#163/* Protocol and I/O related defines */#define PROTO_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */

如果程式的Key設計不合理,客戶端使用大量的query buffer,這會導致redis server比較危險,很容易達到maxmeory限制,導致快取資料被清空、資料無法寫入和oom.

query buffer不受maxmeory限制

模擬100個客戶端,連續寫入大小為500MB(生產建議小於1KB)的Key; redis server設定maxmemory為4gb,但redis實際已用記憶體43gb(見used_memory)。
結論是query buffer使用記憶體不受maxmemory的限制,這BUG已經提給官方, 如不能限制redis使用的記憶體量,
很易導致redis過度使用記憶體,無法控制出現oom.

1234567891011121314151617 127.0.0.1:6390> info memory# Memoryused_memory:46979129016used_memory_human:43.75Gused_memory_rss:49898303488used_memory_rss_human:46.47Gused_memory_peak:54796105584used_memory_peak_human:51.03Gtotal_system_memory:134911881216total_system_memory_human:125.65Gmaxmemory:4294967296maxmemory_human:4.00Gmaxmemory_policy:allkeys-randommem_fragmentation_ratio:1.06mem_allocator:jemalloc-4.0.3## 當client斷開後,rss會馬上釋放記憶體給OS

query buffer佔用記憶體,會計入maxmemory, 如果達到maxmemory限制,會觸發KEY的LRU淘汰或無法寫入新資料。

12 127.0.0.1:6390> set a b(error) OOM command not allowed when used memory > 'maxmemory'.

query buffer使用檢視

如前文介紹,用client list命令,觀察qbuf和qbuf-free兩個欄位,就是client query buffer使用記憶體大小。
如下示例(省去部分欄位)

12345 27.0.0.1:6390> client listid=169 qbuf=128679888 qbuf-free=425984 obl=0 oll=0 omem=0 events=r cmd=NULLid=171 qbuf=128679888 qbuf-free=425984 obl=0 oll=0 omem=0 events=r cmd=NULLid=218 qbuf=128679888 qbuf-free=425984 obl=0 oll=0 omem=0 events=r cmd=NULLid=151 qbuf=128696272 qbuf-free=425984 obl=0 oll=0 omem=0 events=r cmd=NULL

避免query buffer過度使用

  • 禁用大KEY,儘量保證key小於1KB; 雖redis支援512MB大小string。
  • 監控redis記憶體使用,如果忽高忽低,極有可能query buffer引起
  • 核心Redis叢集定期收集client list並分析qbuf的使用量
  • 建議官方提供query buffer size的設定引數,以保證過載保護

Client Output buffer

客戶端輸出快取區:執行命令所返回的結果會儲存到output buffer,返回給客戶端。
每個客戶端都有2個query buffer:

  • 靜態定長16KB的快取區;主要快速儲存返回比較小的結果;如簡單的get等
  • 動態大小緩衝區;儲存返回較大的結果,如大的集合型別:set/list/hash
    因為靜態的buffer,一般無效能和風險影響,這裡簡單介紹。
    123456789101112131415 #define PROTO_REPLY_CHUNK_BYTES (16*1024) /* 16k output buffer *//* With multiplexing we need to take per-client state. * Clients are taken in a linked list. */typedef struct client { uint64_t id; /* Client incremental unique ID. */ redisDb *db; /* Pointer to currently SELECTed DB. */ robj *name; /* As set by CLIENT SETNAME. */ sds querybuf; /* Buffer we use to accumulate client queries. */ list *reply; /* List of reply objects to send to the client. */ /* Response buffer */ int bufpos; char buf[PROTO_REPLY_CHUNK_BYTES];} client;

我們常說的output buffer都是指“動態大小的輸出緩衝區”。

output buffer大小限制

和qeury buffer不同,output buffer提供配置引數”client-output-buffer-limit”設定buffer的使用大小。
下面是limit的設定格式

123 client-output-buffer-limit normal 10mb 5mb 60client-output-buffer-limit slave 256mb 64mb 60client-output-buffer-limit pubsub 32mb 8mb 60

redis對3種不同客戶端型別,可設定對應的buffer limit規則

  • normal: 普通的客戶端
  • slave: 從庫複製,連線到主庫的客戶端
  • pubsub: 釋出/訂閱客戶端

設定的limit規則3個值: hard limit size, soft limit size, soft limit second;
只要客戶端使用output buffer記憶體大小超過hard limit限制,redis會立即關閉此客戶端;
使用buffer記憶體大小超過soft limit,並且持續soft limit秒數,redis也會立即關閉此客戶端。
被關閉客戶端資訊會列印到redis日誌檔案中,格式如下:

123 569:M 18 Jun 21:12:57.775 # Client id=972 addr=127.0.0.1:57934 fd=107 name= age=2 idle=0flags=O db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=366 omem=10492208 events=rw cmd=monitorscheduled to be closed ASAP for overcoming of output buffer limits.

檢視output buffer使用

主要查開client list的obl(靜態定長buffer)
omem: 當前客戶端使用output buffer的記憶體位元組數
如下客戶端執行monitor命令(cmd=monitor), 已使用buffer記憶體是10492208,超過normal的hard limit 10mb
所以被redis關閉。

1 id=972 addr=127.0.0.1:57934 idle=0 flags=O db=0 qbuf=0 qbuf-free=0 obl=0 oll=366 omem=10492208 events=rw cmd=monitor

另外output buffer受maxmemory的限制,基本不會超過maxmemory設定值

合理使用output buffer

因為output buffer是每個客戶端都有,如使用不當,每個佔用1mb * 10000 clients就約使用10G記憶體;
所以要有效限制程式濫用。

  • 對於normal限制儘量小,可避免程式過度使用output buffer.
  • 監控redis used_memory如果抖動嚴重,極有可能
  • 增加slave的limit限制,避免slave同步執行緒被殺,導致無限迴圈同步資料;且slave執行緒和掛載的slave個數相同,理論只有幾個
  • 禁止生產環境使用monitor命令,在高QPS環境下,monitor很快會產生output query使用

如何監控output buffer和query buffer

從前文可見, 如果業務使用redis不當,兩個buffer有可能導致記憶體爆漲,redis快取資料被全部淘汰,甚於出現oom.
那麼怎麼監測兩個buffer的使用情況,提前發現系統的異常行為,並告警就顯得很重要。
這裡提供兩種不同監控採集方法:

  • 通過採集client list輸出,並分別統計求各所有客戶端的(qbuf+ qbuf-free) 和 omem
  • 使用info的clients section中的client_biggest_input_buf和client_longest_output_list兩個指標來監控告警

第一種方法可精確統計當前時刻(buffer完全動態分配回收), redis使用的buffer記憶體容量;但要使用client list命令週期性統計,對於連線數較大redis例項,會導致數十毫秒卡頓(基準測試1w空連線,client list命令耗時約14.5ms);
因為至少每隔幾分鐘要採集一次,在高併發例項下,這樣耗時是不能被接受的,這就是常用的觀察者效應
open-falcon的redis監控外掛redismon, 我們用第二個方法,通過info採集;
兩個指標表示的含義:

  • client_biggest_input_buf:當前例項所有客戶端中,最大query buffer記憶體的位元組數。 告警閾值建議10485760(10M),根據業務再調整。
  • client_longest_output_list:當前例項所有客戶端中, 最長output buffer的個數。告警閾值建議500長度(前文例子中monitor客戶端長度是336,output buffer約10M),不過常用keys,monitor,或複製sync過程,會觸發告警。

兩個指標只能反映,其中使用buffer最厲害那個客戶端的使用的記憶體量;不能直接反映所有客戶端使用兩個buffer記憶體消耗。
但合理設定告警值,也能直接監測試redis系統用於buffer記憶體有異常,並跟蹤定位異常導致的點;
因第二種方法,每分鐘監控採集一次對系統無影響;雖沒前者直觀,也能定位發現問題了,覺得這就是一個tradeoff點。

Redis兩個客戶端的Buffer就簡單介紹這些。後續文章會講redis的監控和buffer相樣的故障問題。

轉載自:https://zhuoroger.github.io/2016/07/30/redis-client-two-buffers/