記一次redis線上問題
閱讀大概需要5分鐘
一個(gè)生產(chǎn)中的問題,建議閱讀四顆星
現(xiàn)象
分析可能原因
調(diào)查原因
概念詳細(xì)介紹
緊急處理辦法
預(yù)防辦法
現(xiàn)象
redis-cluster某個(gè)分片內(nèi)存飆升,明顯比其他分片高很多,而且持續(xù)增長。并且主從的內(nèi)存使用量并不一致。(個(gè)別可能導(dǎo)致服務(wù)無法啟動(dòng))
分析可能原因
對(duì)于 redis 出現(xiàn)這種現(xiàn)象,一般都會(huì)從這個(gè)這幾個(gè)方面考慮
redis-cluster的bug (這個(gè)應(yīng)該不存在)
客戶端的hash(key)有問題,造成分配不均。(redis使用的是crc16, 不會(huì)出現(xiàn)這么不均的情況)
存在個(gè)別大的key-value: 例如一個(gè)包含了幾百萬數(shù)據(jù)set數(shù)據(jù)結(jié)構(gòu)(這個(gè)有可能)
主從復(fù)制出現(xiàn)了問題。
其他原因
調(diào)查原因
經(jīng)查詢,上述1-4都不存在
觀察info信息,有一點(diǎn)引起了懷疑: client_longes_output_list有些異常。
于是理解想到服務(wù)端和客戶端交互時(shí),分別為每個(gè)客戶端設(shè)置了輸入緩沖區(qū)和輸出緩沖區(qū),這部分如果很大的話也會(huì)占用Redis服務(wù)器的內(nèi)存。
從上面的 client_longest_output_list
看,應(yīng)該是輸出緩沖區(qū)占用內(nèi)存較大,也就是有大量的數(shù)據(jù)從Redis服務(wù)器向某些客戶端輸出。
于是,使用 client list
命令(類似于 mysql processlist)redis-cli -h host -p port client list | grep -v "omem=0"
,來查詢輸出緩沖區(qū)不為0的客戶端連接,于是查詢到禍?zhǔn)?monitor
,于是豁然開朗。
monitor
的模型是這樣的,它會(huì)將所有在 Redis 服務(wù)器執(zhí)行的命令進(jìn)行輸出,通常來講 Redis 服務(wù)器的 QPS 是很高的,也就是如果執(zhí)行了 monitor 命令,Redis服務(wù)器在 Monitor 這個(gè)客戶端的輸出緩沖區(qū)又會(huì)有大量“存貨”,也就占用了大量 Redis 內(nèi)存。
概念詳細(xì)介紹
這是 redis 的配置介紹,對(duì)于客戶端內(nèi)存限制
redis 客戶端在于 redis 服務(wù)端相互交互時(shí),服務(wù)端會(huì)為每個(gè)客戶端分別設(shè)置輸入緩存區(qū)和輸出緩存區(qū),我們知道 redis 是基于內(nèi)存的非關(guān)系型數(shù)據(jù)庫,所以這兩部分緩存區(qū)的數(shù)據(jù)依然是保存在內(nèi)存中,如果這兩部分很大的話會(huì)占用 redis 很大的內(nèi)存。模型圖如下圖所示:

使用 info
命令查看當(dāng)前redis客戶端連接數(shù)及當(dāng)前緩存區(qū)的使用量

那么什么是輸入緩存區(qū)和輸出緩存區(qū)呢?
輸入緩存區(qū):用于保存客戶端發(fā)送的請(qǐng)求,輸入緩存區(qū)的大小會(huì)根據(jù)請(qǐng)求數(shù)據(jù)的大小動(dòng)態(tài)變化,1GB 是輸入緩存區(qū)最大的容量,如果超出最大容量該客戶端便會(huì)被關(guān)閉。
輸出緩存區(qū):用于保存執(zhí)行請(qǐng)求后的返回結(jié)果,每個(gè)客戶端有兩個(gè)緩存區(qū),一個(gè)是固定客戶端緩存區(qū)(用于保存那些長度比較小的返回值,如一些比較短的字符串或整數(shù)值);一個(gè)是可變客戶端緩存區(qū)(用于保存那些長度比較大的返回值,如長度比較大的字符串、大集合等)。
服務(wù)器采用軟性限制和硬性限制兩種方式限制輸出緩存區(qū)的大小。
軟性限制:如果軟性限制所設(shè)置的大小小于輸出緩沖區(qū)的大小,且輸出緩沖區(qū)的大小不大于硬性限制所設(shè)置的大小,那么服務(wù)器會(huì)使用客戶端狀態(tài)結(jié)構(gòu)的 obuf_soft_limit_reached_time
屬性來記錄客戶端達(dá)到軟性限制的起始時(shí)間。之后服務(wù)器會(huì)繼續(xù)監(jiān)視客戶端,如果這個(gè)緩沖區(qū)的大小一直超出軟性限制,并且持續(xù)時(shí)間超過服務(wù)器設(shè)定的時(shí)長,那么服務(wù)器將會(huì)關(guān)閉這個(gè)客戶端。相反地,如果輸出緩沖區(qū)的大小在指定時(shí)間范圍之內(nèi)沒有超過軟性限制,那么這個(gè)客戶端不會(huì)被關(guān)閉,并且 obuf_soft_limit_reached_time
屬性的值也會(huì)被設(shè)置為 0。
如果輸出緩存區(qū)的大小大于硬性限制,那么客戶端會(huì)立即關(guān)閉。
在 redis 配置文件中,可以對(duì)軟性限制和硬性限制進(jìn)行設(shè)置:

第一行代碼表示:對(duì)于 普通
客戶端來說,限制為 0,也就是不限制。因?yàn)槠胀蛻舳送ǔ2捎米枞降南?yīng)答模式,通常情況下不會(huì)導(dǎo)致 redis 輸出緩存區(qū)數(shù)據(jù)堆積膨脹。
第二行代碼表示:對(duì)于 slave
客戶端來說,硬性限制是 256M,軟性限制是當(dāng)客戶端緩沖區(qū)大小持續(xù) 60秒 超過 64M,則關(guān)閉客戶端連接。
第三行代碼表示:對(duì)于 Pub/Sub
客戶端(也就是發(fā)布/訂閱模式),硬性限制是 32M,當(dāng)輸出緩沖區(qū)超過 32M 時(shí),會(huì)關(guān)閉連接。軟性限制是當(dāng)客戶端緩沖區(qū)大小持續(xù) 60秒 超過 8M,則關(guān)閉客戶端連接;
緊急處理辦法
進(jìn)行主從切換(主從內(nèi)存使用量不一致),也就是 redis-cluster 的 fail-over 操作,繼續(xù)觀察新的 Master 是否有異常,通過觀察未出現(xiàn)異常。
查找到真正的原因后,也就是 monitor,關(guān)閉掉 monitor
命令的進(jìn)程后,內(nèi)存很快就降下來了。
預(yù)防辦法
修改客戶端源代碼,禁止掉一些危險(xiǎn)的命令(shutdown, flushall, monitor, keys *),當(dāng)然還是可以通過redis-cli來完成;
添加command-rename配置,將一些危險(xiǎn)的命令(flushall, monitor, keys * , flushdb)做rename;
對(duì) redis 使用中遇到的問題分享、學(xué)習(xí)分享很重要。
參考博文:
https://www.iteye.com/blog/carlosfu-2254154