Redis大Key問題如何排查?如何解決?
Redis 大 Key 是指存儲(chǔ)在 Redis 中的鍵值對(duì),其中鍵對(duì)應(yīng)的 value 占用了較大的內(nèi)存空間,或者包含了大量的元素。例如,一個(gè)存儲(chǔ)了數(shù)百萬個(gè)元素的集合(Set)類型的鍵,或者一個(gè)存儲(chǔ)了一個(gè)很大的字符串(長度可能達(dá)到幾十 MB 甚至更大)的鍵都被認(rèn)為是大 Key。
Redis 大 Key 并沒有統(tǒng)一的固定標(biāo)準(zhǔn),當(dāng)字符串存儲(chǔ)了一個(gè)很大的值,例如 10M 以上,或集合存儲(chǔ)了一個(gè)上百萬元素的值,那就認(rèn)為是 Redis 的大 Key 問題了。
1.主要影響
大 Key 問題造成的主要問題是讓 Redis 服務(wù)阻塞,無法處理其他命令的響應(yīng)(客戶端可能因此出現(xiàn)請(qǐng)求超時(shí)的問題)。因?yàn)閷?duì)大 Key 進(jìn)行讀寫操作時(shí),由于需要處理大量的數(shù)據(jù),這些操作可能會(huì)非常耗時(shí),這就會(huì)導(dǎo)致 Redis 主線程被阻塞,無法及時(shí)處理其他客戶端的請(qǐng)求。
2.常見場景
大 Key 的常見場景有以下幾個(gè):
- 緩存大數(shù)據(jù)(圖片和視頻元數(shù)據(jù)):在緩存場景中,可能會(huì)將大型的文件內(nèi)容(如圖片、視頻元數(shù)據(jù))緩存到 Redis 中。例如,一個(gè)視頻分享網(wǎng)站可能會(huì)將視頻的詳細(xì)描述、標(biāo)簽、點(diǎn)贊數(shù)、評(píng)論數(shù)等信息以 JSON 字符串的形式緩存為一個(gè)大 Key,方便快速獲取視頻相關(guān)的所有數(shù)據(jù)。
- 明星或網(wǎng)紅粉絲列表:如果使用 ZSet 來存儲(chǔ)粉絲和關(guān)注列表的話,如果是某些明星或網(wǎng)紅的粉絲列表將會(huì)很大,可能存儲(chǔ)元素超千萬甚至是億級(jí)別。
- 商品頁所有信息:在電商應(yīng)用中,可能需要將商品信息,如商品 ID、名稱、描述、價(jià)格、庫存、圖片、評(píng)價(jià)等都緩存到 Redis 中的話,當(dāng)商品詳情和評(píng)價(jià)(幾十上百萬條評(píng)價(jià))比較多時(shí),這個(gè) Key 就可能變得非常大。
3.排查大Key問題
排查大 Key 的方案有以下幾個(gè)。
(1)BIGKEYS
Redis 自帶的 BIGKEYS 命令可以查詢當(dāng)前 Redis 中所有 key 的信息,對(duì)整個(gè)數(shù)據(jù)庫中的鍵值對(duì)大小情況進(jìn)行統(tǒng)計(jì)分析。它會(huì)查出每種數(shù)據(jù)結(jié)構(gòu)的最大 Key,但不能根據(jù)某個(gè)容量進(jìn)行篩查。比如說,統(tǒng)計(jì)每種數(shù)據(jù)類型的鍵值對(duì)個(gè)數(shù)以及平均大小,如下圖所示:
(2)MEMORY USAGE
Redis 4.0+ 后推出了 MEMORY USAGE 命令,該命令可以返回指定 key 的內(nèi)存使用情況,返回使用的內(nèi)存的字節(jié)數(shù)。通過遍歷所有的 key 并使用此命令,可以找出占用內(nèi)存較大的 key。但需要注意的是,對(duì)于復(fù)雜數(shù)據(jù)結(jié)構(gòu)(如 List、Set 等),MEMORY USAGE 命令返回的是近似值,因?yàn)樗捎贸闃臃绞絹砉浪銉?nèi)存使用,如下圖所示:
(3)OBJECT
OBJECT encoding可以查看鍵值對(duì)象的編碼類型,不同的編碼類型可能暗示了鍵值的復(fù)雜程度和大小。例如,如果一個(gè)字符串類型的鍵采用了 raw 編碼且長度很長,那么它可能是一個(gè)大 Key,如下圖所示:
4.解決大Key問題
大 Key 的解決方案有以下幾個(gè)。
(1)拆分大Key
將大 Key 拆分成多個(gè)小 Key,分別存儲(chǔ)不同部分的數(shù)據(jù)。這樣可以減少單個(gè) Key 的內(nèi)存占用,提高查詢性能,拆分的常用方法有以下幾個(gè):
- 按業(yè)務(wù)邏輯拆分:如果大 Key 是一個(gè)包含大量聚合數(shù)據(jù)的鍵,可以根據(jù)業(yè)務(wù)邏輯將其拆分為多個(gè)小的鍵。例如,對(duì)于一個(gè)包含全品類商品銷售數(shù)據(jù)的大 Key,可以按照品類拆分為多個(gè)小的鍵,每個(gè)鍵只存儲(chǔ)一個(gè)品類的銷售數(shù)據(jù)。
- 按時(shí)間范圍拆分:對(duì)于存儲(chǔ)時(shí)間序列數(shù)據(jù)的大 Key,如日志數(shù)據(jù)或統(tǒng)計(jì)數(shù)據(jù),可以按照時(shí)間范圍進(jìn)行拆分。比如,將一天的日志數(shù)據(jù)拆分為每小時(shí)一個(gè)鍵,這樣可以更靈活地管理和刪除過期數(shù)據(jù)。
(2)使用壓縮算法
對(duì)于可以壓縮的數(shù)據(jù)類型(如字符串),可以使用壓縮算法(如 LZF 等)來減少內(nèi)存占用。Redis 本身支持一些壓縮算法,可以在一定程度上減少大 Key 的內(nèi)存占用。
4.3 使用合適的數(shù)據(jù)結(jié)構(gòu)和存儲(chǔ)方式
- 考慮使用其他存儲(chǔ)系統(tǒng):如果數(shù)據(jù)實(shí)在太大且不適合存儲(chǔ)在 Redis 中,可以考慮將部分?jǐn)?shù)據(jù)轉(zhuǎn)移到其他存儲(chǔ)系統(tǒng),如將大型文件存儲(chǔ)到分布式文件系統(tǒng)(如 MinIO、Ceph 等),只在 Redis 中保留文件的元數(shù)據(jù)或引用。
- 優(yōu)化 Redis 數(shù)據(jù)結(jié)構(gòu)選擇:根據(jù)數(shù)據(jù)的訪問模式和特性,選擇更合適的 Redis 數(shù)據(jù)結(jié)構(gòu)。例如,如果一個(gè)集合類型的大 Key 主要用于判斷元素是否存在,可以考慮使用布谷鳥哈希(Cuckoo Hash)等空間效率更高的數(shù)據(jù)結(jié)構(gòu)替代傳統(tǒng)的集合結(jié)構(gòu)。
(4)設(shè)置合理的過期時(shí)間
如果大 Key 中的數(shù)據(jù)不是一直需要的,可以設(shè)置過期時(shí)間,讓 Redis 在一定時(shí)間后自動(dòng)刪除該 Key。這樣可以避免大 Key 長期占用內(nèi)存,導(dǎo)致內(nèi)存泄漏。
(5)加強(qiáng)監(jiān)控和管理
建立對(duì) Redis 的監(jiān)控系統(tǒng),實(shí)時(shí)監(jiān)測大 Key 的出現(xiàn)和內(nèi)存使用情況。當(dāng)發(fā)現(xiàn)大 Key 或者內(nèi)存占用過高時(shí),及時(shí)發(fā)出預(yù)警,以便采取相應(yīng)的措施進(jìn)行處理。如 Redis Insights、Prometheus 等,設(shè)置對(duì)大 key 和內(nèi)存使用的監(jiān)控指標(biāo)。
注意事項(xiàng):大Key刪除
刪除大 Key 時(shí)要注意,要使用 UNLINK 命令代替 DEL 命令來刪除大 Key。UNLINK 命令會(huì)立即返回,后臺(tái)異步刪除數(shù)據(jù),避免阻塞,如下圖所示:
小結(jié)
Redis 大 Key 問題會(huì)讓 Redis 服務(wù)阻塞,無法響應(yīng)其他命令,可能會(huì)導(dǎo)致客戶端響應(yīng)超時(shí)等問題。排查大 Key 問題可以使用 BIGKEYS、MEMORY USAGE、OBJECT 等命令。它的解決方案有:拆分大 Key、壓縮數(shù)據(jù)、使用合適數(shù)據(jù)結(jié)構(gòu)和存儲(chǔ)方式、設(shè)置合理過期時(shí)間,以及加強(qiáng)監(jiān)控和管理等手段。