Redis的過期策略和內存淘汰策略搞混淆了。
Redis的過期策略
我們都知道,Redis是key-value數據庫,我們可以設置Redis中緩存的key的過期時間。Redis的過期策略就是指當Redis中緩存的key過期了,Redis如何處理。
過期策略通常有以下三種:
- 定時過期:每個設置過期時間的key都需要創建一個定時器,到過期時間就會立即清除。該策略可以立即清除過期的數據,對內存很友好;但是會占用大量的CPU資源去處理過期的數據,從而影響緩存的響應時間和吞吐量。
- 惰性過期:只有當訪問一個key時,才會判斷該key是否已過期,過期則清除。該策略可以最大化地節省CPU資源,卻對內存非常不友好。極端情況可能出現大量的過期key沒有再次被訪問,從而不會被清除,占用大量內存。
- 定期過期:每隔一定的時間,會掃描一定數量的數據庫的expires字典中一定數量的key,並清除其中已過期的key。該策略是前兩者的一個折中方案。通過調整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和內存資源達到最優的平衡效果。
(expires字典會保存所有設置了過期時間的key的過期時間數據,其中,key是指向鍵空間中的某個鍵的指針,value是該鍵的毫秒精度的UNIX時間戳表示的過期時間。鍵空間是指該Redis集群中保存的所有鍵。)
Redis中同時使用了惰性過期和定期過期兩種過期策略。
Redis的內存淘汰策略
Redis的內存淘汰策略是指在Redis的用於緩存的內存不足時,怎么處理需要新寫入且需要申請額外空間的數據。
- noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯。
- allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key。
- allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key。
- volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的key。
- volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個key。
- volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的key優先移除。
總結
Redis的內存淘汰策略的選取並不會影響過期的key的處理。內存淘汰策略用於處理內存不足時的需要申請額外空間的數據;過期策略用於處理過期的緩存數據。
Redis開發建議
最后附上Redis的一些開發規范和建議
1. 冷熱數據分離,不要將所有數據全部都放到Redis中
雖然Redis支持持久化,但是Redis的數據存儲全部都是在內存中的,成本昂貴。建議根據業務只將高頻熱數據存儲到Redis中【QPS大於5000】,對於低頻冷數據可以使用MySQL/ElasticSearch/MongoDB等基於磁盤的存儲方式,不僅節省內存成本,而且數據量小在操作時速度更快、效率更高!
2. 不同的業務數據要分開存儲
不要將不相關的業務數據都放到一個Redis實例中,建議新業務申請新的單獨實例。因為Redis為單線程處理,獨立存儲會減少不同業務相互操作的影響,提高請求響應速度;同時也避免單個實例內存數據量膨脹過大,在出現異常情況時可以更快恢復服務! 在實際的使用過程中,redis最大的瓶頸一般是CPU,由於它是單線程作業所以很容易跑滿一個邏輯CPU,可以使用redis代理或者是分布式方案來提升redis的CPU使用率。
3. 存儲的Key一定要設置超時時間
如果應用將Redis定位為緩存Cache使用,對於存放的Key一定要設置超時時間!因為若不設置,這些Key會一直占用內存不釋放,造成極大的浪費,而且隨着時間的推移會導致內存占用越來越大,直到達到服務器內存上限!另外Key的超時長短要根據業務綜合評估,而不是越長越好!
4. 對於必須要存儲的大文本數據一定要壓縮后存儲
對於大文本【+超過500字節】寫入到Redis時,一定要壓縮后存儲!大文本數據存入Redis,除了帶來極大的內存占用外,在訪問量高時,很容易就會將網卡流量占滿,進而造成整個服務器上的所有服務不可用,並引發雪崩效應,造成各個系統癱瘓!
5. 線上Redis禁止使用Keys正則匹配操作
Redis是單線程處理,在線上KEY數量較多時,操作效率極低【時間復雜度為O(N)】,該命令一旦執行會嚴重阻塞線上其它命令的正常請求,而且在高QPS情況下會直接造成Redis服務崩潰!如果有類似需求,請使用scan命令代替!
6. 可靠的消息隊列服務
Redis List經常被用於消息隊列服務。假設消費者程序在從隊列中取出消息后立刻崩潰,但由於該消息已經被取出且沒有被正常處理,那么可以認為該消息已經丟失,由此可能會導致業務數據丟失,或業務狀態不一致等現象發生。
為了避免這種情況,Redis提供了RPOPLPUSH命令,消費者程序會原子性的從主消息隊列中取出消息並將其插入到備份隊列中,直到消費者程序完成正常的處理邏輯后再將該消息從備份隊列中刪除。同時還可以提供一個守護進程,當發現備份隊列中的消息過期時,可以重新將其再放回到主消息隊列中,以便其它的消費者程序繼續處理。
7. 謹慎全量操作Hash、Set等集合結構
在使用HASH結構存儲對象屬性時,開始只有有限的十幾個field,往往使用HGETALL獲取所有成員,效率也很高,但是隨着業務發展,會將field擴張到上百個甚至幾百個,此時還使用HGETALL會出現效率急劇下降、網卡頻繁打滿等問題【時間復雜度O(N)】,此時建議根據業務拆分為多個Hash結構;或者如果大部分都是獲取所有屬性的操作,可以將所有屬性序列化為一個STRING類型存儲!同樣在使用SMEMBERS操作SET結構類型時也是相同的情況!
8. 根據業務場景合理使用不同的數據結構類型
目前Redis支持的數據庫結構類型較多:字符串(String),哈希(Hash),列表(List),集合(Set),有序集合(Sorted Set), Bitmap, HyperLogLog和地理空間索引(geospatial)等,需要根據業務場景選擇合適的類型。
常見的如:String可以用作普通的K-V、計數類;Hash可以用作對象如商品、經紀人等,包含較多屬性的信息;List可以用作消息隊列、粉絲/關注列表等;Set可以用於推薦;Sorted Set可以用於排行榜等!
9. 命名規范
雖然說Redis支持多個數據庫(默認32個,可以配置更多),但是除了默認的0號庫以外,其它的都需要通過一個額外請求才能使用。所以用前綴作為命名空間可能會更明智一點。
另外,在使用前綴作為命名空間區隔不同key的時候,最好在程序中使用全局配置來實現,直接在代碼里寫前綴的做法要嚴格避免,這樣可維護性實在太差了。
如:系統名:業務名:業務數據:其他
但是注意,key的名稱不要過長,盡量清晰明了,容易理解,需要自己衡量
10. 線上禁止使用monitor命令
禁止生產環境使用monitor命令,monitor命令在高並發條件下,會存在內存暴增和影響Redis性能的隱患
11. 禁止大string
核心集群禁用1mb的string大key(雖然redis支持512MB大小的string),如果1mb的key每秒重復寫入10次,就會導致寫入網絡IO達10MB;
12. redis容量
單實例的內存大小不建議過大,建議在10~20GB以內。redis實例包含的鍵個數建議控制在1kw內,單實例鍵個數過大,可能導致過期鍵的回收不及時。
13. 可靠性
需要定時監控redis的健康情況:使用各種redis健康監控工具,實在不行可以定時返回redis的info信息。客戶端連接盡量使用連接池(長鏈接和自動重連)
