天天看點

Redis 的key設計技巧&&緩存問題

Redis 的key設計技巧

1.1 原主鍵列進行查詢

比照關系資料庫的設計:

1): 把表名轉換為key字首.

2): 第2段放置用于區分區key的字段–對應mysql中的主鍵的列名,如userid

3): 第3段放置主鍵值,如2,3,4…., a , b ,c

4): 第4段,寫要存儲的列名

Redis 的key設計技巧&&緩存問題

對該表設定redis的key如下設計:

Redis 的key設計技巧&&緩存問題

1.2原非主鍵列進行查詢

兩步進行:

1)先根據非主鍵列設定一個key,存儲的是key的值

2)然後根據查到的key再到第一步中的key中取查詢具體的值.

即:

在關系型資料中,除主鍵外,還有可能其他列也步驟查詢,

如上表中, username 也是極頻繁查詢的,往往這種列也是加了索引的.

轉換到k-v資料中,則也要相應的生成一條按照該列為主的key-value

Set user:username:lurenjia:uid 1

這樣,我們可以根據username:lurenjia:uid ,查出userid=1,

再查user:1:password/email …

完成了根據使用者名來查詢使用者資訊

Redis 使用中的常見問題

2.1 緩存穿透(緩存擊穿)

2.1.1什麼是緩存穿透?

一般的緩存系統,都是按照key去緩存查詢,如果不存在對應的value,就應該去後端系統查找(比如DB)。如果key對應的value是一定不存在的,并且對該key并發請求量很大,就會對後端系統造成很大的壓力。這就叫做緩存穿透。

緩存穿透簡單說來就是緩存根本沒用上了,緩存中不存在然後去DB查,然後DB查沒有,然後不緩存,然後來了緩存中不存在又去DB查.

2.1.2 如何避免

1:對查詢結果為空的情況也進行緩存,緩存時間設定短一點,或者該key對應的資料insert了之後清理緩存。

2: 使用互斥鎖(mutex key)

業界比較常用的做法,是使用mutex。簡單地來說,就是在緩存失效的時候(判斷拿出來的值為空),不是立即去load db, 而是先使用緩存工具的某些帶成功操作傳回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一個mutex key,當操作傳回成功時,再進行load db的操作并回設緩存;否則,就重試整個get緩存的方法.

代碼如下:

SETNX,是「SET if Not eXists」的縮寫,也就是隻有不存在的時候才設定,可以利用它來實作鎖的效果。在redis2之前版本未實作setnx的過期時間,是以這裡給出兩種版本代碼參考

public String get(key) {
      String value = redis.get(key);
      if (value == null) { //代表緩存值過期
          //設定3min的逾時,防止del操作失敗的時候,下次緩存過期一直不能load db
            if (redis.setnx(key_mutex, ,  * ) == ) {  //代表設定成功
                      value = db.get(key);
                      redis.set(key, value, expire_secs);
                      redis.del(key_mutex);
                    } else {  //這個時候代表同時候的其他線程已經load db并回設到緩存了,這時候重試擷取緩存值即可
                        sleep();
                        get(key);  //重試
                    }
          } else {
              return value;      
          }
 }
           

8.2 緩存雪崩

8.2.1 什麼是緩存雪崩?

當緩存伺服器重新開機或者大量緩存集中在某一個時間段失效,這樣在失效的時候,也會給後端系統(比如DB)帶來很大壓力。

即:

如果是資料從緩存中查詢,如果沒有則從mysql中查詢,然後緩存到緩存中.這種模式在并發量并非太高或資料操作效率很高的情況下基本沒有什麼問題。

但是如果if(緩存失效 && 恰好遇到并發量很高 && 資料庫操作時間長) then?

1. 緩存失效

2. 第一個程序去資料庫擷取新資料,假如包括SQL+程式邏輯耗時5S

3. 這5S内,第二個、第三個…第N個都隻是擷取到已失效的緩存,于是也都連接配接資料庫…

4. 結果顯而易見,資料庫鎖表 -> 資料庫累計大量程序 -> 直至資料庫挂掉!

8.2.2 如何避免?

1:在緩存失效後,通過加鎖或者隊列來控制讀資料庫寫緩存的線程數量。比如對某個key隻允許一個線程查詢資料和寫緩存,其他線程等待。

2:不同的key,設定不同的過期時間,讓緩存失效的時間點盡量均勻。

3:做二級緩存,A1為原始緩存,A2為拷貝緩存,A1失效時,可以通路A2,A1緩存失效時間設定為短期,A2設定為長期.

4: 添加一個标記,類似檔案鎖,用于判斷此時程式是否正在更新緩存。

若是,則直接傳回舊緩存(标記有設定失效時間,避免由于程式錯誤導緻标記未删除而引起的緩存不更新問題)

若否,則設定一個标記,然後進行資料擷取及緩存更新,最後删除标記。

繼續閱讀