轉錄自:https://baijiahao.baidu.com/s?id=1660009541007805174&wfr=spider&for=pc
簡介:
c語言開發的開源高性能鍵值對的記憶體資料庫(非關系型資料庫),可作資料庫、緩存、消息中間件等。
因為是記憶體資料庫,讀寫速度非常快,官方提供的資料可以達到 100000+ 的 QPS(每秒内的查詢次數)。
資料類型:string、hash、list、set、zset(sorted set)。
支援資料持久化。可做分布式鎖。可做消息中間件使用,支援釋出訂閱。
使用多路複用 IO 模型,非阻塞 IO。
資料類型:
String
類型的值最大能存儲 512M,二進制安全是以可以存序列化的對象。
hash
常用指令:hget、hset、hgetall
list
Redis List 的是實作是一個雙向連結清單常用指令:lpush、rpush、lpop、rpop、lrange
Set
不允許重複的元素。常用指令:zadd、zrange、zrem、zcard 等。
Sorted Set
Sorted Set關聯了一個 Double 類型權重的參數 Score,使得集合中的元素能夠按照 Score 進行有序排列,Redis 正是通過分數來為集合中的成員進行從小到大的排序。
内部使用 HashMap 和跳躍表(skipList)來保證資料的存儲和有序,HashMap 裡放的是成員到 Score 的映射。
而跳躍表裡存放的是所有的成員,排序依據是 HashMap 裡存的 Score,使用跳躍表的結構可以獲得比較高的查找效率,并且在實作上比較簡單。
結合spring boot使用時的核心注解
@[email protected]@CacheEvict
①@Cacheable
根據方法的請求參數對其結果進行緩存:
Key:緩存的 Key,可以為空,如果指定要按照 SPEL 表達式編寫,如果不指定,則按照方法的所有參數進行組合。Value:緩存的名稱,必須指定至少一個(如 @Cacheable (value='user')或者 @Cacheable(value={'user1','user2'}))Condition:緩存的條件,可以為空,使用 SPEL 編寫,傳回 true 或者 false,隻有為 true 才進行緩存。
②@CachePut
根據方法的請求參數對其結果進行緩存,和 @Cacheable 不同的是,它每次都會觸發真實方法的調用。參數描述見上。
③@CacheEvict
根據條件對緩存進行清空:
Key:同上。Value:同上。Condition:同上。allEntries:是否清空所有緩存内容,預設為 false,如果指定為 true,則方法調用後将立即清空所有緩存。beforeInvocation:是否在方法執行前就清空,預設為 false,如果指定為 true,則在方法還沒有執行的時候就清空緩存。預設情況下,如果方法執行抛出異常,則不會清空緩存。
常見問題
緩存雪崩:
同一時間大面積失效,瞬間 Redis 跟沒有一樣,那這個數量級别的請求直接打到資料庫幾乎是災難性的。
如何避免雪崩:
緩存失效時間要錯開,不要設定成一樣,避免同一時間大面積失效。
如果是叢集部署,将熱點資料均勻分布在不同的redis庫也可以避免,或者設定熱點資料永不過期,有更新操作再更新緩存。
緩存穿透:
緩存穿透是指緩存和資料庫中都沒有的資料,而使用者(黑客)不斷發起請求。
緩存擊穿:
緩存雪崩是因為大面積的緩存失效,打崩了 DB。
而緩存擊穿不同的是緩存擊穿是指一個 Key 非常熱點,在不停地扛着大量的請求,大并發集中對這一個點進行通路,當這個 Key 在失效的瞬間,持續的大并發直接落到了資料庫上,就在這個 Key 的點上擊穿了緩存。
如何解決緩存穿透:
緩存穿透可以在接口層加校驗,比如使用者鑒權,參數校驗,不合法的直接return.
另外布隆過濾器也可以很好的預防穿透發生。原理是高效的資料結構和算法快速判斷出你這個 Key 是否在資料庫中存在,不存在你 return 就好了,
如何解決緩存擊穿:
緩存擊穿的話,設定熱點資料永不過期,或者加上互斥鎖就搞定了。
publicstatic String getData(String key)throws InterruptedException {
//從Redis查詢資料
String result = getDataByKV(key);
//參數校驗
if (StringUtils.isBlank(result)) {
try {
//獲得鎖
if (reenLock.tryLock()) {
//去資料庫查詢
result = getDataByDB(key);
//校驗if
(StringUtils.isNotBlank(result)) {
//插進緩存
setDataToKV(key, result);
}
} else {
//睡一會再拿
Thread.sleep(100L);
result = getData(key);
}
} finally {
//釋放鎖
reenLock.unlock();
}
}return result;
}
問:Redis 這麼快,為什麼還是單線程的?
Redis 确實是單程序單線程的模型,因為 Redis 完全是基于記憶體的操作,CPU 不是 Redis 的瓶頸,Redis 的瓶頸最有可能是機器記憶體的大小或者網絡帶寬。
既然單線程容易實作,而且 CPU 不會成為瓶頸,那就順理成章的采用單線程的方案了
淘汰機制
簡介 | 描述 |
---|---|
volatile-lru | 從已設定過期時間的KV集中優先對最近最少使用的資料淘汰 |
volatile-ttl | 從已設定過期時間的KV集中優先對剩餘時間短的資料淘汰 |
volatile-random | 從已設定過期時間的KV集中随機選擇資料淘汰 |
allkeys-lru | 從所有KV集中優先對最近最少使用的資料淘汰 |
allKeys-random | 從所有KV集中随機選擇資料淘汰 |
noeviction | 不淘汰,若超過最大記憶體,傳回錯誤資訊 |
volatile-lfu (v4.0) | 從已設定過期時間的K集中優先對通路頻率最少,即最不經常使用的 KV 淘汰 |
allkeys-lfu (v4.0) | 從所有KV集中優先對通路頻率最少,即最不經常使用的 KV 淘汰 |
持久化
資料緩存在了記憶體中,但是會周期性的把更新的資料寫入磁盤或者把修改操作寫入追加的記錄檔案中,以保證資料的持久化。
兩種持久化政策
RDB
快照形式是直接把記憶體中的資料把偶才能到一個dump檔案中,定時儲存。
工作原理
當 Redis 需要做持久化時,Redis 會 fork 一個子程序,子程序将資料寫到磁盤上一個臨時 RDB 檔案中。
當子程序完成寫臨時檔案後,将原來的 RDB 替換掉,這樣的好處是可以 copy-on-write。即使遇上問題,也可以随時将資料集還原到不同的版本。RDB 非常适合災難恢複。
缺點:
如果你需要盡量避免在伺服器故障時丢失資料,那麼RDB不合适你。
AOF
把所有的對redis的伺服器進行修改的指令都存放到檔案裡,指令的集合。Redis預設是快照RDB的持久化方式。
當 Redis 重新開機的時候,它會優先使用 AOF 檔案來還原資料集,因為 AOF 檔案儲存的資料集通常比 RDB 檔案所儲存的資料集更完整。你甚至可以關閉持久化功能,讓資料隻在伺服器運作時存。
AOF的預設政策是每秒鐘 Fsync 一次,在這種配置下,就算發生故障停機,也最多丢失一秒鐘的資料。
工作原理
每次有資料修改發生時都會寫入AOF檔案。appendfsync everysec #每秒鐘同步一次,該政策為AOF的預設政策。
缺點:AOF 的檔案體積通常要大于 RDB 檔案的體積。AOF 的速度可能會慢于 RDB。
總結:Redis 支援同時開啟 RDB 和 AOF,系統重新開機後,Redis 會優先使用 AOF 來恢複資料,這樣丢失的資料會最少。
主從複制
Redis 單節點存在單點故障問題,為了解決單點問題,一般都需要對 Redis 配置從節點,然後使用哨兵來監聽主節點的存活狀态,如果主節點挂掉,從節點能繼續提供緩存功能
從節點僅提供讀操作,主節點提供寫操作。對于讀多寫少的狀況,可給主節點配置多個從節點,進而提高響應效率。
複制的過程
從節點執行 ,slaveof[masterIP][masterPort]儲存主節點資訊。從節點中的定時任務發現主節點資訊,建立和主節點的 Socket 連接配接。從節點發送 Ping 信号,主節點傳回 Pong,兩邊能互相通信。連接配接建立後,主節點将所有資料發送給從節點(資料同步)。主節點把目前的資料同步給從節點後,便完成了複制的建立過程。接下來,主節點就會持續的把寫指令發送給從節點,保證主從資料一緻性。
哨兵
一旦主節點當機,從節點晉升為主節點,同時需要修改應用方的主節點位址,還需要指令所有從節點去複制新的主節點,整個過程需要人工幹預。主節點的寫能力受到單機的限制。主節點的存儲能力受到單機的限制。原生複制的弊端在早期的版本中也會比較突出,比如:Redis 複制中斷後,從節點會發起 psync。此時如果同步不成功,則會進行全量同步,主庫執行全量備份的同時,可能會造成毫秒或秒級的卡頓。
解決方案就需要哨兵啦......