Redis核心知識點
Redis
總體概述
“兩大維度”就是指系統維度和應用維度,“三大主線”也就是指高效能、高可靠和高可擴充套件(可以簡稱為“三高”)。
問題畫像圖:
基礎概念
Redis本質上是一個 Key-Value 型別的記憶體資料庫,支援String、List、Set、Sorted Set、hashes。
Redis 的通訊協議是 Redis 序列化協議,簡稱 RESP。它具有如下特徵:1.在 TCP 層;2.二進位制安全;3.基於請求 - 響應模式。
Redis 會將事務中的多個命令一次性、按順序一次執行,在執行期間可以保證不會中斷事務去執行其他命令。Redis 的事務滿足一致性和隔離性,但不支援原子性和永續性,事務不會回滾。
鍵值對儲存結構
為了實現從鍵到值的快速訪問,Redis 使用了一個雜湊表來儲存所有鍵值對。一個雜湊表,其實就是一個數組,陣列的每個元素稱為一個雜湊桶。所以,我們常說,一個雜湊表是由多個雜湊桶組成的,每個雜湊桶中儲存了鍵值對資料。
在下圖中,可以看到,雜湊桶中的 entry 元素中儲存了key和value指標,分別指向了實際的鍵和值,這樣一來,即使值是一個集合,也可以通過*value指標被查詢到。
Hash衝突
Redis 解決雜湊衝突的方式,就是鏈式雜湊。鏈式雜湊也很容易理解,就是指同一個雜湊桶中的多個元素用一個連結串列來儲存,它們之間依次用指標連線。
如果雜湊表裡寫入的資料越來越多,雜湊衝突可能也會越來越多,這就會導致某些雜湊衝突鏈過長,進而導致這個鏈上的元素查詢耗時長,效率降低。
所以,Redis 會對雜湊表做 rehash 操作。rehash 也就是增加現有的雜湊桶數量,讓逐漸增多的 entry 元素能在更多的桶之間分散儲存,減少單個桶中的元素數量,從而減少單個桶中的衝突。那具體怎麼做呢?
其實,為了使 rehash 操作更高效,Redis 預設使用了兩個全域性雜湊表:雜湊表 1 和雜湊表 2。一開始,當你剛插入資料時,預設使用雜湊表 1,此時的雜湊表 2 並沒有被分配空間。隨著資料逐步增多,Redis 開始執行 rehash,這個過程分為三步:
- 給雜湊表 2 分配更大的空間,例如是當前雜湊表 1 大小的兩倍;
- 把雜湊表 1 中的資料重新對映並拷貝到雜湊表 2 中;
- 釋放雜湊表 1 的空間。
底層資料結構
資料型別
String
SDS動態字串,二進位制安全,最大512M
1.開發者不用擔心字串變更造成的記憶體溢位問題。
2.常數時間複雜度獲取字串長度len欄位。
3.空間預分配free欄位,會預設留夠一定的空間防止多次重分配記憶體。
List
雙向連結串列上擴充套件了頭、尾節點、元素數等屬性
1.可以直接獲得頭、尾節點。
2.常數時間複雜度得到連結串列長度。
3.雙向連結串列
hash
在陣列+連結串列的基礎上,進行了一些rehash優化等
1.Redis的Hash採用鏈地址法來處理衝突
2.雜湊表節點採用單鏈表結構。
3.rehash優化,漸進式rehash
set
通過 hashtable 實現
無序的自動去重
zset
內部使用 HashMap 和跳躍表(skipList)
HashMap 裡放的是成員到 Score 的對映。而跳躍表裡存放的是所有的成員
執行緒模型
Redis 在單執行緒下還可以支援高併發的一個重要原因就是 Redis 的執行緒模型:基於非阻塞的IO多路複用機制。(epoll/select/poll)
redis 內部使用檔案事件處理器 file event handler,這個檔案事件處理器是單執行緒的,所以 redis 才叫做單執行緒的模型。它採用 IO 多路複用機制同時監聽多個 socket,根據 socket 上的事件來選擇對應的事件處理器進行處理。
檔案事件處理器
-
多個 socket
-
IO 多路複用程式
-
select/poll
- 採用輪詢方式掃描檔案描述符(select陣列,poll連結串列),
檔案描述符越多,效能越差 - 需要複製大量的控制代碼資料結構,產生巨大的開銷
- 需要遍歷整個陣列/連結串列,才能找到發生事件的控制代碼
- 水平觸發,報告了發生事件的控制代碼若未被處理,下次還將上報
- 採用輪詢方式掃描檔案描述符(select陣列,poll連結串列),
-
epoll
- EPOLLLT和EPOLLET兩種觸發模式
- 沒有最大併發連線的限制,能開啟的FD的上限遠大於1024
- 效率提升,不是輪詢的方式,不會隨著FD數目的增加效率下降。只有活躍可用的FD才會呼叫callback函式
-
-
檔案事件分派器
-
事件處理器
- 連線應答處理器
- 命令請求處理器
- 命令回覆處理器
多個 socket 可能會併發產生不同的操作,每個操作對應不同的檔案事件,但是 IO 多路複用程式會監聽多個 socket,會將 socket 產生的事件放入佇列中排隊,事件分派器每次從佇列中取出一個事件,把該事件交給對應的事件處理器進行處理。
來看客戶端與 redis 的一次通訊過程:
客戶端 socket01 向 redis 的 server socket 請求建立連線,此時 server socket 會產生一個 AE_READABLE 事件,IO 多路複用程式監聽到 server socket 產生的事件後,將該事件壓入佇列中。檔案事件分派器從佇列中獲取該事件,交給連線應答處理器。連線應答處理器會建立一個能與客戶端通訊的 socket01,並將該 socket01 的 AE_READABLE 事件與命令請求處理器關聯。
假設此時客戶端傳送了一個 set key value 請求,此時 redis 中的 socket01 會產生 AE_READABLE 事件,IO 多路複用程式將事件壓入佇列,此時事件分派器從佇列中獲取到該事件,由於前面 socket01 的 AE_READABLE 事件已經與命令請求處理器關聯,因此事件分派器將事件交給命令請求處理器來處理。命令請求處理器讀取 socket01 的 key value 並在自己記憶體中完成 key value 的設定。操作完成後,它會將 socket01 的 AE_WRITABLE 事件與命令回覆處理器關聯。
如果此時客戶端準備好接收返回結果了,那麼 redis 中的 socket01 會產生一個 AE_WRITABLE 事件,同樣壓入佇列中,事件分派器找到相關聯的命令回覆處理器,由命令回覆處理器對 socket01 輸入本次操作的一個結果,比如 ok,之後解除 socket01 的 AE_WRITABLE 事件與命令回覆處理器的關聯。
IO 多路複用程式
Linux 中的 IO 多路複用機制是指一個執行緒處理多個 IO 流,就是我們經常聽到的 select/epoll 機制。簡單來說,在 Redis 只執行單執行緒的情況下,該機制允許核心中,同時存在多個監聽套接字和已連線套接字。核心會一直監聽這些套接字上的連線請求或資料請求。一旦有請求到達,就會交給 Redis 執行緒處理,這就實現了一個 Redis 執行緒處理多個 IO 流的效果。
下圖就是基於多路複用的 Redis IO 模型。圖中的多個 FD 就是剛才所說的多個套接字。Redis 網路框架呼叫 epoll 機制,讓核心監聽這些套接字。此時,Redis 執行緒不會阻塞在某一個特定的監聽或已連線套接字上,也就是說,不會阻塞在某一個特定的客戶端請求處理上。正因為此,Redis 可以同時和多個客戶端連線並處理請求,從而提升併發性。
為了在請求到達時能通知到 Redis 執行緒,select/epoll 提供了基於事件的回撥機制,即針對不同事件的發生,呼叫相應的處理函式。
高併發/可用
持久化
Redis 的持久化主要有兩大機制,即 AOF(Append Only File)日誌和 RDB 快照。
AOF
AOF 即 Append-only file:把所有對 Redis 伺服器進行修改的命令儲存到 aof 檔案中,命令的集合。(命令寫入/檔案同步/檔案重寫)。AOF 是寫後日志,“寫後”的意思是 Redis 是先執行命令,把資料寫入記憶體,然後才記錄日誌,如下圖所示:
寫後日志這種方式,就是先讓系統執行命令,只有命令能執行成功,才會被記錄到日誌中,否則,系統就會直接向客戶端報錯。所以,Redis 使用寫後日志這一方式的一大好處是,可以避免出現記錄錯誤命令的情況。
除此之外,AOF 還有一個好處:它是在命令執行後才記錄日誌,所以不會阻塞當前的寫操作。
AOF 也有兩個潛在的風險。
首先,如果剛執行完一個命令,還沒有來得及記日誌就宕機了,那麼這個命令和相應的資料就有丟失的風險。如果此時 Redis 是用作快取,還可以從後端資料庫重新讀入資料進行恢復,但是,如果 Redis 是直接用作資料庫的話,此時,因為命令沒有記入日誌,所以就無法用日誌進行恢復了。
其次,AOF 雖然避免了對當前命令的阻塞,但可能會給下一個操作帶來阻塞風險。這是因為,AOF 日誌也是在主執行緒中執行的,如果在把日誌檔案寫入磁碟時,磁碟寫壓力大,就會導致寫盤很慢,進而導致後續的操作也無法執行了。
刷盤策略
這兩個風險都是與系統刷盤有關的,AOF 機制提供了三個刷盤策略,也就是 AOF 配置項 appendfsync 的三個可選值。
- Always,同步寫回:每個寫命令執行完,立馬同步地將日誌寫回磁碟;
- Everysec,每秒寫回:每個寫命令執行完,只是先把日誌寫到 AOF 檔案的記憶體緩衝區,每隔一秒把緩衝區中的內容寫入磁碟;
- No,作業系統控制的寫回:每個寫命令執行完,只是先把日誌寫到 AOF 檔案的記憶體緩衝區,由作業系統決定何時將緩衝區內容寫回磁碟。
優缺點如下:
AOF 重寫機制
簡單來說,AOF 重寫機制就是在重寫時,Redis 根據資料庫的現狀建立一個新的 AOF 檔案,也就是說,讀取資料庫中的所有鍵值對,然後對每一個鍵值對用一條命令記錄它的寫入。
為什麼重寫機制可以把日誌檔案變小呢? 實際上,重寫機制具有“多變一”功能。所謂的“多變一”,也就是說,舊日誌檔案中的多條命令,在重寫後的新日誌中變成了一條命令。
AOF 日誌由主執行緒寫回不同,重寫過程是由後臺子程序 bgrewriteaof 來完成的,這也是為了避免阻塞主執行緒,導致資料庫效能下降。
總結來說,每次 AOF 重寫時,Redis 會先執行一個記憶體拷貝,用於重寫;然後,使用兩個日誌保證在重寫過程中,新寫入的資料不會丟失。而且,因為 Redis 採用額外的執行緒進行資料重寫,所以,這個過程並不會阻塞主執行緒。
RDB記憶體快照
Redis 的資料都在記憶體中,為了提供所有資料的可靠性保證,它執行的是全量快照,也就是說,把記憶體中的所有資料都記錄到磁碟中。這樣做的好處是,一次性記錄了所有資料,一個都不少。
Redis 提供了兩個命令來生成 RDB 檔案,分別是 save 和 bgsave。
- save:在主執行緒中執行,會導致阻塞;
- bgsave:建立一個子程序,專門用於寫入 RDB 檔案,避免了主執行緒的阻塞,這也是 Redis RDB 檔案生成的預設配置。
處理複製時新產生的資料
Redis 會藉助作業系統提供的寫時複製技術(Copy-On-Write, COW),在執行快照的同時,正常處理寫操作。
簡單來說,bgsave 子程序是由主執行緒 fork 生成的,可以共享主執行緒的所有記憶體資料。bgsave 子程序執行後,開始讀取主執行緒的記憶體資料,並把它們寫入 RDB 檔案。
此時,如果主執行緒對這些資料也都是讀操作(例如圖中的鍵值對 A),那麼,主執行緒和 bgsave 子程序相互不影響。但是,如果主執行緒要修改一塊資料(例如圖中的鍵值對 C),那麼,這塊資料就會被複制一份,生成該資料的副本。然後,bgsave 子程序會把這個副本資料寫入 RDB 檔案,而在這個過程中,主執行緒仍然可以直接修改原來的資料。
這既保證了快照的完整性,也允許主執行緒同時對資料進行修改,避免了對正常業務的影響。
優缺點
AOF:
優點:
- 相比於 RDB,AOF 更加安全,默認同步策略為每秒同步一次,最差就失去一秒的資料。
- 根據關注點不同,AOF 提供了不同的同步策略。
- AOF 檔案是以 append-only 方式寫入,相比如RDB 全量寫入的方式,它沒有任何磁碟定址的開銷,寫入效能非常高。
缺點:
- 由於 AOF 日誌檔案是命令級別的,所以相比於 RDB 緊緻的二進位制檔案而言它的載入速度會慢些。
- AOF 開啟後,支援的寫 QPS 會比 RDB 支援的寫 QPS 低。
RDB
優點:
- 由於 RDB 檔案是一個非常緊湊的二進位制檔案,所以載入的速度回快於 AOF 方式;
- fork 子程序(bgsave)方式,不會阻塞
RDB 檔案代表著 Redis 伺服器的某一個時刻的全量資料,所以它非常適合做冷備份和全量複製的場景
缺點:沒辦法做到實時持久化,會存在丟資料的風險。定時執行持久化過程,如果在這個過程中伺服器崩潰了,則會導致這段時間的資料全部丟失。
觸發機制
-
全量複製
-
debug reload:進行一個debug級別的重啟 不需要清空記憶體 並且在該過程仍會觸發RDB檔案的生成
-
shutdown:進行關閉的時候會進行 RDB檔案的生成
多機實現
主從複製(保障高併發)
預設情況下,Redis所有節點都是主節點,節點之間互不干涉,而主從複製的節點則是劃分了主節點(master)和從節點(slave)。
Redis 提供了主從庫模式,以保證資料副本的一致,主從庫之間採用的是讀寫分離的方式。
讀操作:主庫、從庫都可以接收;
寫操作:首先到主庫執行,然後,主庫將寫操作同步給從庫。
主從庫間如何進行第一次同步?
當我們啟動多個 Redis 例項的時候,它們相互之間就可以通過 replicaof(Redis 5.0 之前使用 slaveof)命令形成主庫和從庫的關係,之後會按照三個階段完成資料的第一次同步。
哨兵機制
基本流程
哨兵其實就是一個執行在特殊模式下的 Redis 程序,主從庫例項執行的同時,它也在執行。哨兵主要負責的就是三個任務:監控、選主(選擇主庫)和通知。
- 監控是指哨兵程序在執行時,週期性地給所有的主從庫傳送 PING 命令,檢測它們是否仍然線上執行。如果從庫沒有在規定時間內響應哨兵的 PING 命令,哨兵就會把它標記為“下線狀態”;同樣,如果主庫也沒有在規定時間內響應哨兵的 PING 命令,哨兵就會判定主庫下線,然後開始自動切換主庫的流程。
- 選主。 主庫掛了以後,哨兵就需要從很多個從庫裡,按照一定的規則選擇一個從庫例項,把它作為新的主庫。這一步完成後,現在的叢集裡就有了新主庫。
- 通知。在執行通知任務時,哨兵會把新主庫的連線資訊發給其他從庫,讓它們執行 replicaof 命令,和新主庫建立連線,並進行資料複製。同時,哨兵會把新主庫的連線資訊通知給客戶端,讓它們把請求操作發到新主庫上。
三項任務與目標如下圖:
另一個角度闡釋來說:
功能
1.叢集監控,即時刻監控著redis的master和slave程序是否是在正常工作。
2.訊息通知,就是說當它發現有redis例項有故障的話,就會發送訊息給管理員。
3.故障自動轉移
- 1.多個sentinel發現master有問題
- 2.選舉一個sentinel作為領導
- 3.選舉一個slave作為master
- 4.通知其它slave作為新的master的slave
- 5.通知客戶端主從變化
- 6.等待老的master復活成為新的master的slave
4.充當配置中心,如果發生了故障轉移,它會通知將master的新地址寫在配置中心告訴客戶端。
下線判斷
哨兵對主庫的下線判斷有“主觀下線”和“客觀下線”兩種。
哨兵程序會使用 PING 命令檢測它自己和主、從庫的網路連線情況,用來判斷例項的狀態。如果哨兵發現主庫或從庫對 PING 命令的響應超時了,那麼,哨兵就會先把它標記為“主觀下線”
在判斷主庫是否下線時,不能由一個哨兵說了算,只有大多數的哨兵例項,都判斷主庫已經“主觀下線”了,主庫才會被標記為“客觀下線”
新庫選擇
一般來說,把哨兵選擇新主庫的過程稱為“篩選 + 打分”。簡單來說,我們在多個從庫中,先按照一定的篩選條件,把不符合條件的從庫去掉。然後,我們再按照一定的規則,給剩下的從庫逐個打分,將得分最高的從庫選為新主庫,如下圖所示:
哨兵叢集
基於 pub/sub 機制的哨兵叢集組成
哨兵例項之間可以相互發現,要歸功於 Redis 提供的 pub/sub 機制,也就是釋出 / 訂閱機制。哨兵只要和主庫建立起了連線,就可以在主庫上釋出訊息了,比如說釋出它自己的連線資訊(IP 和埠)。同時,它也可以從主庫上訂閱訊息,獲得其他哨兵釋出的連線資訊。當多個哨兵例項都在主庫上做了釋出和訂閱操作後,它們之間就能知道彼此的 IP 地址和埠。然後多個哨兵例項之間就可以相互建立連線。
哨兵是如何知道從庫的 IP 地址和埠的呢?這是由哨兵向主庫傳送 INFO 命令來完成的。就像下圖所示,哨兵 2 給主庫傳送 INFO 命令,主庫接受到這個命令後,就會把從庫列表返回給哨兵。接著,哨兵就可以根據從庫列表中的連線資訊,和每個從庫建立連線,並在這個連線上持續地對從庫進行監控。哨兵 1 和 3 可以通過相同的方法和從庫建立連線。
基於 pub/sub 機制的客戶端事件通知
從本質上說,哨兵就是一個執行在特定模式下的 Redis 例項,只不過它並不服務請求操作,只是完成監控、選主和通知的任務。所以,每個哨兵例項也提供 pub/sub 機制,客戶端可以從哨兵訂閱訊息。哨兵提供的訊息訂閱頻道有很多,不同頻道包含了主從庫切換過程中的不同關鍵事件。
可以讓客戶端從哨兵這裡訂閱訊息了。具體的操作步驟是,客戶端讀取哨兵的配置檔案後,可以獲得哨兵的地址和埠,和哨兵建立網路連線。然後,我們可以在客戶端執行訂閱命令,來獲取不同的事件訊息。
哨兵leader選舉,在投票過程中,任何一個想成為 Leader 的哨兵,要滿足兩個條件:第一,拿到半數以上的贊成票;第二,拿到的票數同時還需要大於等於哨兵配置檔案中的 quorum 值。以 3 個哨兵為例,假設此時的 quorum 設定為 2,那麼,任何一個想成為 Leader 的哨兵只要拿到 2 張贊成票,就可以了。
切片叢集
切片叢集,也叫分片叢集,就是指啟動多個 Redis 例項組成一個叢集,然後按照一定的規則,把收到的資料劃分成多份,每一份用一個例項來儲存。回到我們剛剛的場景中,如果把 25GB 的資料平均分成 5 份(當然,也可以不做均分),使用 5 個例項來儲存,每個例項只需要儲存 5GB 資料。如下圖所示:
Redis Cluster 方案採用雜湊槽(Hash Slot,接下來我會直接稱之為 Slot),來處理資料和例項之間的對映關係。在 Redis Cluster 方案中,一個切片叢集共有 16384 個雜湊槽,這些雜湊槽類似於資料分割槽,每個鍵值對都會根據它的 key,被對映到一個雜湊槽中。
具體的對映過程分為兩大步:首先根據鍵值對的 key,按照CRC16 演算法計算一個 16 bit 的值;然後,再用這個 16bit 值對 16384 取模,得到 0~16383 範圍內的模數,每個模數代表一個相應編號的雜湊槽。
雜湊槽分配:
我們在部署 Redis Cluster 方案時,可以使用 cluster create 命令建立叢集,此時,Redis 會自動把這些槽平均分佈在叢集例項上。例如,如果叢集中有 N 個例項,那麼,每個例項上的槽個數為 16384/N 個。
當然, 我們也可以使用 cluster meet 命令手動建立例項間的連線,形成叢集,再使用 cluster addslots 命令,指定每個例項上的雜湊槽個數。
客戶端如何定位資料?
Redis 例項會把自己的雜湊槽資訊發給和它相連線的其它例項,來完成雜湊槽分配資訊的擴散。當例項之間相互連線後,每個例項就有所有雜湊槽的對映關係了。
客戶端收到雜湊槽資訊後,會把雜湊槽資訊快取在本地。當客戶端請求鍵值對時,會先計算鍵所對應的雜湊槽,然後就可以給相應的例項傳送請求了。
但是,在叢集中,例項和雜湊槽的對應關係並不是一成不變的,最常見的變化有兩個:
- 在叢集中,例項有新增或刪除,Redis 需要重新分配雜湊槽;
- 為了負載均衡,Redis 需要把雜湊槽在所有例項上重新分佈一遍。
例項之間還可以通過相互傳遞訊息,獲得最新的雜湊槽分配資訊,但是,客戶端是無法主動感知這些變化的。這就會導致,它快取的分配資訊和最新的分配資訊就不一致了。
Redis Cluster 方案提供了一種重定向機制,所謂的“重定向”,就是指,客戶端給一個例項傳送資料讀寫操作時,這個例項上並沒有相應的資料,客戶端要再給一個新例項傳送操作命令。
具體細節:接收命令的節點會計算出命令要處理的資料庫鍵屬於哪個槽,並檢查這個槽是否指派給了自己。若為否返回MOVED錯誤,指引客戶端轉向(redirect)至正確的節點。
-
哨兵(Sentinel)
-
1.哨兵叢集至少要 3 個節點,來確保自己的健壯性。
2.redis主從 + sentinel的架構,是不會保證資料的零丟失的,它是為了保證redis叢集的高可用。-
功能
-
1.叢集監控,即時刻監控著redis的master和slave程序是否是在正常工作。
-
2.訊息通知,就是說當它發現有redis例項有故障的話,就會發送訊息給管理員。
-
3.故障自動轉移
-
1.多個sentinel發現master有問題
- 2.選舉一個sentinel作為領導
- 3.選舉一個slave作為master
- 4.通知其它slave作為新的master的slave
- 5.通知客戶端主從變化
- 6.等待老的master復活成為新的master的slave
-
-
4.充當配置中心,如果發生了故障轉移,它會通知將master的新地址寫在配置中心告訴客戶端。
-
-
叢集
-
當資料量過大一個主機放不下的時候,就需要對資料進行分割槽,將key按照一定的規則進行計算,並將key對應的value分配到指定的Redis例項上,這樣的模式簡稱Redis叢集。
-
主從節點的分散式器群,具有複製分片和高可用特性
-
針對海量資料+高併發+高可用的場景
-
資料分佈演算法
- hash演算法(大量快取重建)
- 一致性hash演算法(自動快取遷移)+虛擬節點(自動負載均衡)
- redis cluster的hash slot演算法
-
步驟
- 將各個獨立的節點連線起來,構成一個包含多個節點的叢集。
- 叢集的整個資料庫被分為16384個槽(slot),將槽分配給不同節點。
- 接收命令的節點會計算出命令要處理的資料庫鍵屬於哪個槽,並檢查這個槽是否指派給了自己。若為否返回MOVED錯誤,指引客戶端轉向(redirect)至正確的節點。
-
常見問題
快取雪崩
-
快取雪崩是指快取中資料大批量到過期時間,而查詢資料量巨大,引起資料庫壓力過大甚至down機。(Redis崩潰導致的快取失效也包括在內)
-
解決方法
-
事前
- 1.快取資料的過期時間設定隨機,防止同一時間大量資料過期現象發生。
- 2.如果快取資料庫是分散式部署,將熱點資料均勻分佈在不同Redis庫中。
- 3.設定熱點資料永遠不過期。
- 4.redis高可用,主從+哨兵,redis cluster,避免全盤崩潰
-
事中
- 本地ehcache快取 + hystrix限流&降級,避免MySQL被打死
-
事後
- redis持久化,快速恢復快取資料
-
快取擊穿
- 快取擊穿是指快取中沒有但資料庫中有的資料(熱點key),這時由於併發使用者特別多,同時讀快取沒讀到資料,又同時去資料庫去取資料,引起資料庫壓力瞬間增大,造成過大壓力。
- 解決方法:1.設定熱點資料永遠不過期。2.加互斥鎖。
快取穿透
-
快取穿透是指快取和資料庫中都沒有的資料,而使用者不斷髮起請求 。
-
解決方法
- 1.介面層增加校驗,如使用者鑑權校驗,id做基礎校驗,id<=0的直接攔截;
- 2.空值設定快取,key-null;
- 3.布隆過濾器。
過期策略
定時刪除
定期刪除
- redis 預設是每隔 100ms 就隨機抽取一些設定了過期時間的 key,檢查其是否過期,如果過期就刪除。
惰性刪除
- 獲取 key 的時候,如果此時 key 已經過期,就刪除,不會返回任何東西
記憶體淘汰機制
- allkeys-lru:當記憶體不足以容納新寫入資料時,在鍵空間中,移除最近最少使用的 key
- allkeys-random:當記憶體不足以容納新寫入資料時,在鍵空間中,隨機移除某個 key
- volatile-lru:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,移除最近最少使用的 key
- volatile-random:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,隨機移除某個 key
- volatile-ttl:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,有更早過期時間的 key 優先移除。
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int CACHE_SIZE;
/**
* 傳遞進來最多能快取多少資料
* @param cacheSize 快取大小
*/
public LRUCache(int cacheSize) {
// true 表示讓 linkedHashMap 按照訪問順序來進行排序,最近訪問的放在頭部,最老訪問的放在尾部。
super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
CACHE_SIZE = cacheSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
// 當 map中的資料量大於指定的快取個數的時候,就自動刪除最老的資料。
return size() > CACHE_SIZE;
}
}