Redis分佈式緩存規範

適用範圍

本規範主要面向用友雲的Redis開發規範,從鍵值設計、命令使用、客戶端使用、相關工具等方面進行說明,遵循此規範可以減少使用Redis過程帶來的問題。

 

一、緩存設計

1.【推薦】避免緩存穿透

  • 數據庫中未查詢到的數據,可在Redis中設置特殊標識,以避免因緩存中無數據而導致每次請求均達到數據庫。

2.【推薦】避免緩存雪崩

  • 當大量緩存集中在某一個時間段失效,這樣在失效的時候也會給數據庫帶來很大壓力。

3.【推薦】避免緩存擊穿

  • 某個key的緩存過期後,同一時間內有大量的請求均訪問該key,由於緩存過期,大量的請求均會訪問數據庫,並重建緩存;重建緩存的過程加鎖,保證只有一個人執行,其他人等待。

4.【推薦】可以進行適當的緩存預熱

  • 對於上線後可能會有大量讀請求的應用,在上線之前可預先將數據寫入緩存中

5.【推薦】數據一致性問題

  • 數據源發生變更時可能導致緩存中數據與數據源中數據不一致,應根據實際業務需求來選擇適當的緩存更新策略:

a)   主動更新:在數據源發生變更時同步更新緩存數據或將緩存數據過期。一致性高,維護成本較高。

b)   被動刪除:根據緩存設置的過期時間有Redis負責數據的過期刪除。一致性較低,維護成本較低。

c)   推薦策略:主動更新,數據源發生變更時將緩存數據過期。緩存和DB的更新不在同一個事務問題:

    • 利用Spring的事務管理機制,在事務管理器上註冊一個事務提交後回調,在回調方法中進行緩存清理;
    • 使用異步消息隊列,並且支持重試補償機制。(如果緩存裏數據時間與數據庫時間不能匹配,意味着另外一個服務更新了該數據,那麼就先從DB裏讀取最新數據版本,然後在新版本上提交數據)

二、鍵值設計

1.強制key不要包含特殊字符

  • Redis以“\n”作爲命令結束符,以空格作爲命令和參數分隔符。反例:包含空格、換行、單雙引號以及其他轉義字符

2.【強制】拒絕bigkey(防止網卡流量、慢查詢)

  • 反例:一個包含200萬個元素的list。非字符串的bigkey,不要使用del刪除,使用hscan、sscan、zscan方式漸進式刪除,同時要注意防止bigkey過期時間自動刪除問題(例如一個200萬的zset設置1小時過期,會觸發del操作,造成阻塞,而且該操作不會出現在慢查詢中(latency可查))

3.【推薦】key值可讀性和可管理性

  • 以業務名(或數據庫名)爲前綴(防止key衝突),用冒號分隔,比如[模塊名]:[業務描述]:[ID]

4.【推薦】key值簡潔性

  • 保證語義的前提下,控制key的長度,當key較多時,內存佔用也不容忽視。

5.【推薦】根據業務場景合理使用不同數據結構類型

  • 目前Redis支持的數據庫結構類型較多:字符串(String),哈希(Hash),列表(List),集合(Set),有序集合(Sorted Set), Bitmap, HyperLogLog和地理空間索引(geospatial)等,需要根據業務場景選擇合適的類型,常見的如:String可以用作普通的K-V、計數類;Hash可以用作對象如商品、經紀人等,包含較多屬性的信息;List可以用作消息隊列等;Set可以用於推薦;Sorted Set可以用於排行榜等。

6.【推薦】控制key的生命週期

  • redis不是垃圾桶,建議使用expire設置過期時間(條件允許可以打散過期時間,防止集中過期),不過期的數據重點關注idletime。

7.【推薦】冷熱數據分離,不要將所有數據全部都放到Redis

  • 雖然Redis支持持久化,但是Redis的數據存儲全部都是在內存中的,成本昂貴,建議根據業務只將高頻熱數據存儲到Redis中。

三、命令使用

1.【強制】線上禁止使用keys命令

  • Redis是單線程處理,在線上KEY數量較多時,操作效率極低【時間複雜度爲O(N)】,該命令一旦執行會嚴重阻塞線上其它命令的正常請求,而且在高QPS情況下會直接造成Redis服務崩潰。如果有類似需求,請使用scan命令代替。

2.【強制】禁用命令

  • 禁止線上使用flushall、flushdb等,flushall、flushdb會清空redis數據。通過redis的rename機制禁掉命令,或者使用scan的方式漸進式處理。

3.【推薦】O(N)命令關注N的數量

  • 例如hgetall、lrange、smembers、zrange、sinter等並非不能使用,但是需要明確N的值。有遍歷的需求可以使用hscan、sscan、zscan代替。

4.【推薦】合理使用select

  • redis的多數據庫較弱,使用數字進行區分,很多客戶端支持較差,同時多業務用多數據庫實際還是單線程處理,會有干擾。

5.【推薦】使用批量操作提高效率

  • 原生命令mset、mget,非原生命令pipline;但要注意控制一次批量操作的元素個數(例如500以內,實際也和元素字節數有關)

6.【推薦】Redis事務功能較弱,不建議過多使用

  • Redis的事務功能較弱(不支持回滾),而且集羣版本(自研和官方)要求一次事務操作的key必須在一個slot上(可以使用hashtag功能解決)

7.【推薦】Redis集羣版本在使用Lua上有特殊要求:

  • Redis從2.6版本開始引入對Lua腳本的支持
  • 所有key都應該由 KEYS 數組來傳遞,redis.call/pcall 裏面調用的redis命令,key的位置,必須是KEYS array, 否則直接返回error
  • Redis集羣對多key操作有限制,要求命令中所有的key都屬於一個slot,纔可以被執行。

8.【推薦】必要情況下使用monitor命令時,要注意不要長時間使用

  • monitor命令在開啓的情況下會降低redis的吞吐量,根據壓測結果大概會降低redis50%的吞吐量,越多客戶端開啓該命令,吞吐量下降會越多。

四、客戶端使用

1.【推薦】避免多個應用使用一個實例

  • 避免多個應用使用一個Redis實例。不要將不相關的業務數據都放到一個實例中,建議新業務申請新的單獨實例。因爲Redis爲單線程處理,獨立存儲會減少不同業務相互操作的影響,提高請求響應速度;同時也避免單個實例內存數據量膨脹過大,在出現異常情況時可以更快恢復服務,公共數據做服務化。

2.【推薦】使用連接池

  • 使用帶有連接池的數據庫,可以有效控制連接,同時提高效率

3.【推薦】添加熔斷功能

  • 高併發下建議客戶端添加熔斷功能(例如netflix hystrix)

4.【強制】設置合理的密碼

  • 設置合理的密碼,如有必要可以使用SSL加密訪問(阿里雲Redis支持)

5.【推薦】根據業務類型選擇淘汰策略

  • 根據自身業務類型,選好maxmemory-policy(最大內存淘汰策略),設置好過期時間。默認策略是volatile-lru,即超過最大內存後,在過期鍵中使用lru算法進行key的剔除,保證不過期數據不被刪除,但是可能會出現OOM問題。
  • 其他策略如下:

a)     allkeys-lru:根據LRU算法刪除鍵,不管數據有沒有設置超時屬性,直到騰出足夠空間爲止。

b)     allkeys-random:隨機刪除所有鍵,直到騰出足夠空間爲止。

c)      volatile-random:隨機刪除過期鍵,直到騰出足夠空間爲止。

d)     volatile-ttl:根據鍵值對象的ttl屬性,刪除最近將要過期數據。如果沒有,回退到noeviction策略。

e)     noeviction:不會剔除任何數據,拒絕所有寫入操作並返回客戶端錯誤信息"(error) OOM command not allowed when used memory",此時Redis只響應讀操作。

6.【強制】設置連接池時,如果非默認database,直接在URI中指定,不能每次查詢selectDb

五、相關工具推薦

1.【推薦】數據同步

  • redis間數據同步可以使用:redis-port

2.【推薦】big key搜索

  • redis大key搜索工具

3.【推薦】熱點key尋找

  • 內部實現使用monitor,建議短時間使用;facebook的redis-faina 。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章