Redis數據淘汰策略
Redis官方給的警告,當內存不足時,Redis會根據配置的緩存策略淘汰部分Keys,以保證寫入成功。當無淘汰策略時或沒有找到適合淘汰的Key時,Redis直接返回out of memory錯誤。
- 最大緩存配置:在 redis 中,允許用戶設置最大使用內存大小是
maxmemory 512G
redis 提供6種數據淘汰策略:
volatile-lru
:從已設置過期時間的數據集中挑選最近最少使用的數據淘汰volatile-lfu
:從已設置過期的Keys中,刪除一段時間內使用次數最少使用的volatile-ttl
:從已設置過期時間的數據集中挑選最近將要過期的數據淘汰volatile-random
:從已設置過期時間的數據集中隨機選擇數據淘汰allkeys-lru
:從數據集中挑選最近最少使用的數據淘汰allkeys-lfu
:從所有Keys中,刪除一段時間內使用次數最少使用的allkeys-random
:從數據集中隨機選擇數據淘汰no-enviction
(驅逐):禁止驅逐數據(不採用任何淘汰策略。默認即爲此配置),針對寫操作,返回錯誤信息
建議:瞭解了Redis的淘汰策略之後,在平時使用時應儘量主動設置/更新key的expire時間,主動剔除不活躍的舊數據,有助於提升查詢性能
Redis持久化
簡介
數據存放於:
- 內存:高效、斷電(關機)內存數據會丟失
- 硬盤:讀寫速度慢於內存,斷電數據不會丟失
RDB
- RDB:是redis的默認持久化機制。 RDB相當於照快照,保存的是一種狀態。
幾十G數據-->幾KB快照
- 快照是
默認
的持久化方式。這種方式是就是將內存中數據以快照的方式寫入到二進制文件中,默認的文件名爲dump.rdb。
優點:
- 快照保存數據極快、還原數據極快
- 適用於災難備份
缺點:
- 小內存機器不適合使用,RDB機制符合要求就會照快照
快照條件:
- 1、服務器正常關閉時
./bin/redis-cli shutdown
- 2、key滿足一定條件,會進行快照
vim redis.conf 搜索save
:/save
save 900 1 //每900秒(15分鐘)至少1個key發生變化,產生快照
save 300 10 //每300秒(5分鐘)至少10個key發生變化,產生快照
save 60 10000 //每60秒(1分鐘)至少10000個key發生變化,產生快照
AOF
- 由於
快照方式是在一定間隔時間做一次
的,所以如果redis 意外down 掉的話,就會丟失最後一次快照後的所有修改。如果應用要求不能丟失任何修改的話,可以採用aof 持久化方式。 - Append-only file:aof 比快照方式有更好的持久化性,是由於在使用aof 持久化方式時,redis 會將每一個收到的寫命令都通過write 函數追加到文件中(默認是appendonly.aof)。當redis 重啓時會通過重新執行文件中保存的寫命令來在內存中重建整個數據庫的內容。
有三種方式如下(默認是:每秒 fsync 一次)
appendonly yes //啓用 aof 持久化方式
- appendfsync always //收到寫命令就立即寫入磁盤,最慢,但是保證完全的持久化
- appendfsync everysec //每秒鐘寫入磁盤一次,在性能和持久化方面做了很好的折中
- appendfsync no //完全依賴 os,性能最好,持久化沒保證
產生的問題:
- aof 的方式也同時帶來了另一個問題。持久化文件會變的越來越大。例如我們調用 incr test命令 100 次,文件中必須保存全部的 100 條命令,其實有 99 條都是多餘的。
Redis緩存與數據庫一致性
解決方案
一、實時同步
- 對強一致要求比較高的,應採用實時同步方案,即查詢緩存查詢不到再從DB查詢,保存到緩存;更新緩存時,先更新數據庫,再將緩存的設置過期(建議不要去更新緩存內容,直接設置緩存過期)。
- @Cacheable:查詢時使用,注意Long類型需轉換爲Sting類型,否則會拋異常
- @CachePut:更新時使用,使用此註解,一定會從DB上查詢數據
- @CacheEvict:刪除時使用;
- @Caching:組合用法
二、異步隊列
- 對於併發程度較高的,可採用異步隊列的方式同步,可採用kafka等消息中間件處理消息生產和消費。
三、使用阿里的同步工具canal - canal實現方式是模擬mysql slave和master的同步機制,監控DB bitlog的日誌更新來觸發緩存的更新,此種方法可以解放程序員雙手,減少工作量,但在使用時有些侷限性。
- master將改變記錄到二進制日誌(binary log)中(這些記錄叫做二進制日誌事件,binary log events,可以通過show binlog events進行查看);
- slave將master的binary log events拷貝到它的中繼日誌(relay log);
- slave重做中繼日誌中的事件,將改變反映它自己的數據。
- canal模擬mysql slave的交互協議,僞裝自己爲mysql slave,向mysql master發送dump協議
- mysql master收到dump請求,開始推送binary log給slave(也就是canal)
- canal解析binary log對象(原始爲byte流)
四、採用UDF自定義函數的方式
- 面對mysql的API進行編程,利用觸發器進行緩存同步,但UDF主要是c/c++語言實現,學習成本高。
總結
緩存穿透
-
緩存穿透
是指查詢一個一定不存在的數據,由於緩存是不命中時需要從數據庫查詢,查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到數據庫去查詢,造成緩存穿透。 -
解決辦法
: 持久層查詢不到就緩存空結果,查詢時先判斷緩存中是否exists(key) ,如果有直接返回空,沒有則查詢後返回,注意insert時需清除查詢的key,否則即便DB中有值也查詢不到(當然也可以設置空緩存的過期時間)
緩存雪崩
緩存雪崩
:緩存大量失效的時候,引發大量查詢數據庫。解決辦法
:①用鎖/分佈式鎖或者隊列串行訪問 ②緩存失效時間均勻分佈
熱點key
熱點key
:某個key訪問非常頻繁,當key失效的時候有大量線程來構建緩存,導致負載增加,系統崩潰。
解決辦法:
- ①使用鎖,單機用synchronized,lock等,分佈式用分佈式鎖。
- ②緩存過期時間不設置,而是設置在key對應的value裏。如果檢測到存的時間超過過期時間則異步更新緩存。
- ③在value設置一個比過期時間t0小的過期時間值t1,當t1過期的時候,延長t1並做更新緩存操作。
- ④設置標籤緩存,標籤緩存設置過期時間,標籤緩存過期後,需異步地更新實際緩存
常見面試題
如何選擇合適的持久化方式?
- 一般來說, 如果想達到足以媲美PostgreSQL的數據安全性,你應該同時使用兩種持久化功能。在這種情況下,當 Redis 重啓的時候會優先載入AOF文件來恢復原始的數據,因爲在通常情況下AOF文件保存的數據集要比RDB文件保存的數據集要完整。
- 如果你非常關心你的數據, 但仍然可以承受數分鐘以內的數據丟失,那麼你可以只使用RDB持久化。
- 有很多用戶都只使用AOF持久化,但並不推薦這種方式,因爲定時生成RDB快照(snapshot)非常便於進行數據庫備份, 並且 RDB 恢復數據集的速度也要比AOF恢復的速度要快,除此之外,使用RDB還可以避免AOF程序的bug。
- 如果你只希望你的數據在服務器運行的時候存在,你也可以不使用任何持久化方式。
Redis持久化數據和緩存怎麼做擴容?
- 如果Redis被當做緩存使用,使用一致性哈希實現動態擴容縮容。
- 如果Redis被當做一個持久化存儲使用,必須使用固定的keys-to-nodes映射關係,節點的數量一旦確定不能變化。否則的話(即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 key的過期時間和永久有效分別怎麼設置?
- EXPIRE和PERSIST命令。
我們知道通過expire來設置key 的過期時間,那麼對過期的數據怎麼處理呢?
除了緩存服務器自帶的緩存失效策略之外(Redis默認的有6中策略可供選擇),我們還可以根據具體的業務需求進行自定義的緩存淘汰,常見的策略有兩種:
- 定時去清理過期的緩存;
- 當有用戶請求過來時,再判斷這個請求所用到的緩存是否過期,過期的話就去底層系統得到新數據並更新緩存。
兩者各有優劣,第一種的缺點是維護大量緩存的key是比較麻煩的,第二種的缺點就是每次用戶請求過來都要判斷緩存失效,邏輯相對比較複雜!具體用哪種方案,大家可以根據自己的應用場景來權衡。
MySQL裏有2000w數據,redis中只存20w的數據,如何保證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的寫命令會返回錯誤信息(但是讀命令還可以正常返回。)或者你可以配置內存淘汰機制,當Redis達到內存上限時會沖刷掉舊的內容
你知道的越多,你不知道的越多。
有道無術,術尚可求,有術無道,止於術。
如有其它問題,歡迎大家留言,我們一起討論,一起學習,一起進步