快取有哪些型別?
快取是?并發場景下提?熱點資料訪問性能的?個有效?段,在開發項?時會經常使?到,
快取的型別分為:本地快取、分布式快取和多級快取,
本地快取:
本地快取就是在行程的記憶體中進?快取,?如我們的 JVM 堆中,可以? LRUMap 來實作,也可以使?Ehcache 這樣的?具來實作,
本地快取是記憶體訪問,沒有遠程互動開銷,性能最好,但是受限于單機容量,?般快取較?且?法擴展,
分布式快取:
分布式快取可以很好得解決這個問題,
分布式快取?般都具有良好的?平擴展能?,對較?資料量的場景也能應付?如,缺點就是需要進?遠程請求,性能不如本地快取,
多級快取:
為了平衡這種情況,實際業務中?般采?多級快取,本地快取只保存訪問頻率最?的部分熱點資料,其他的熱點資料放在分布式快取中,
這也是最常?的快取?案,單考單?的快取?案往往難以撐住很多?并發的場景,
Redis 為什么是單執行緒的,單執行緒為什么執?速度這么快?
官方FAQ表示,因為Redis是基于記憶體的操作,CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機器記憶體的大小或者網路帶寬,既然單執行緒容易實作,而且CPU不會成為瓶頸,那就順理成章地采用單執行緒的方案了(畢竟采用多執行緒會有很多麻煩!)Redis利用佇列技術將并發訪問變為串行訪問
1)純記憶體操作,避免?量訪問資料庫,減少直接讀取磁盤資料,redis將資料儲存在記憶體??,讀寫資料的時候都不會受到硬碟 I/O 速度的限制,所以速度快,
2)單執行緒操作,避免了不必要的上下?切換和競爭條件,也不存在多行程或者多執行緒導致的切換?消耗CPU,不?去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現死鎖?導致的性能消耗
3)采?了?阻塞I/O多路復?機制,非阻塞IO優點:
- 速度快,因為資料存在記憶體中,類似于HashMap,HashMap的優勢就是查找和操作的時間復雜度都是O(1)
- 支持豐富資料型別,支持string,list,set,sorted set,hash
- 支持事務,操作都是原子性,所謂的原子性就是對資料的更改要么全部執行,要么全部不執行
- 豐富的特性:可用于快取,訊息,按key設定過期時間,過期后將會自動洗掉如何解決redis的并發競爭key問題
4)資料結構簡單,對資料操作也簡單,Redis中的資料結構是專門進行設計的;
5)使用底層模型不同,它們之間底層實作方式以及與客戶端之間通信的應用協議不一樣,Redis直接自己構建了VM 機制 ,因為一般的系統呼叫系統函式的話,會浪費一定的時間去移動和請求;

Redis 的執行緒模型
Redis 內部使??件事件處理器 file event handler ,這個?件事件處理器是單執行緒的,所以 Redis才叫做單執行緒的模型,它采? IO 多路復?機制同時監聽多個 Socket,根據 Socket 上的事件來選擇對應的事件處理器進?處理,
?件事件處理器的結構包含 4 個部分:
- 多個 Socket
- IO 多路復?程式
- ?件事件分派器
- 事件處理器(連接應答處理器、命令請求處理器、命令回復處理器)

多個 Socket 可能會并發產?不同的操作,每個操作對應不同的?件事件,但是 IO 多路復?程式會監聽多個 Socket,會將 Socket 產?的事件放?佇列中排隊,事件分派器每次從佇列中取出?個事件,把該事件交給對應的事件處理器進?處理,
Redis資料結構底層實作

String:
(1)Simple dynamic string(SDS)的資料結構:
SDS 類似于 Java 中的 ArrayList,可以通過預分配冗余空間的?式來減少記憶體的頻繁分配,
struct sdshdr{
//記錄buf陣列中已使?位元組的數量
//等于 SDS 保存字串的?度
int len;
//記錄 buf 陣列中未使?位元組的數量
int free;
//位元組陣列,?于保存字串
char buf[]; }
它的優點: (1)不會出現字串變更造成的記憶體溢位問題
(2)獲取字串?度時間復雜度為1 (3)空間預分配, 惰性空間釋放free欄位,會默認留夠?定的空間防?多次重分配記憶體
應?場景: String 快取結構體?戶資訊,計數
Hash:
陣列+鏈表的基礎上,進?了?些rehash優化;
1.Reids的Hash采?鏈地址法來處理沖突,然后它沒有使?紅?樹優化,
2.哈希表節點采?單鏈表結構,
3.rehash優化 (采?分?治之的思想,將龐?的遷移?作量劃分到每?次CURD中,避免了服務繁忙)
應?場景: 保存結構體資訊可部分獲取不?序列化所有欄位
List: 有序串列
應?場景: (1):?如twitter的關注串列,粉絲串列等都可以?Redis的list結構來實作
(2):list的實作為?個雙向鏈表,即可以?持反向查找和遍歷
- 訊息佇列:Redis的鏈表結構,可以輕松實作阻塞佇列,可以使?左進右出的命令組成來完成佇列的設計,?如:資料的?產者可以通過Lpush命令從左邊插?資料,多個資料消費者,可以使?BRpop命令阻塞的“搶”串列尾部的資料,
- ?章串列或者資料分?展示的應?,
?如,我們常?的博客?站的?章串列,當?戶量越來越多時,?且每?個?戶都有??的?章串列,?且當?章多時,都需要分?展示,這時可以考慮使?Redis的串列,串列不但有序同時還?持按照范圍內獲取元素,可以完美解決分?查詢功能,??提?查詢效率,
Set: 是?序集合,會?動去重的那種,
內部實作是?個 value為null的HashMap,實際就是通過計算hash的?式來快速排重的,這也是set能提供判斷?個成員 是否在集合內的原因,
直接基于 Set 將系統?需要去重的資料扔進去,?動就給去重了,如果你需要對?些資料進?快速的全域去重,你當然也可以基于 JVM 記憶體?的 HashSet 進?去重,但是如果你的某個系統部署在多臺機器上呢?得基于Redis進?全域的 Set 去重,
可以基于 Set 玩?交集、并集、差集的操作,?如交集吧,我們可以把兩個?的好友串列整?個交集,看看倆?的共同好友是誰?對吧,反正這些場景?較多,因為對?很快,操作也簡單,兩個查詢?個Set搞定,
Zset:
內部使?HashMap和跳躍表(SkipList)來保證資料的存盤和有序,HashMap?放的是成員到score的映射,?跳躍表?存放的是所有的成員,排序依據是HashMap?存的score,使?跳躍表的結構可以獲得?較?的查找效率,并且在實作上?較簡單,
跳表:每個節點中維持多個指向其他節點的指標,從?達到快速訪問節點的?的
應?場景:
- 實作延時佇列:使?sortedset,拿時間戳作為score,訊息內容作為key調?zadd來?產訊息,消費者?zrangebyscore指令獲取N秒之前的資料輪詢進?處理,
- 排?榜:有序集合經典使?場景,例如視頻?站需要對?戶上傳的視頻做排?榜,榜單維護可能是多??:按照時間、按照播放量、按斬訓得的贊數等,
為啥redis zset使?跳躍鏈表?不?紅?樹實作?
(1):skiplist的復雜度和紅?樹?樣,?且實作起來更簡單,
(2):在并發環境下紅?樹在插?和洗掉時需要rebalance,性能不如跳表,
異步佇列
如何使?Redis做異步佇列?
?般使?list結構作為佇列,rpush?產訊息,lpop消費訊息,當lpop沒有訊息的時候,要適當sleep?會再重試,
可不可以不?sleep呢?
list還有個指令叫blpop,在沒有訊息的時候,它會阻塞住直到訊息到來,
能不能?產?次消費多次呢?
使?pub/sub主題訂閱者模式,可以實作1:N的訊息佇列,
pub/sub有什么缺點?
在消費者下線的情況下,?產的訊息會丟失,得使?專業的訊息佇列如rabbitmq等,
延時佇列
使?sortedset,拿時間戳作為score,訊息內容作為key調?zadd來?產訊息,消費者?zrangebyscore指令獲取N秒之前的資料輪詢進?處理,
keys命令
1億個key,其中有10w個key是以某個固定的已知的前綴開頭的,如果將它們全部找出來?
使?keys指令可以掃出指定模式的key串列, 如果這個redis正在給線上的業務提供服務,那使?keys指令會有什么問題?
- keys指令:redis的單執行緒的,keys指令會導致執行緒阻塞?段時間,線上服務會停頓,直到指令執?完畢,服務才能恢復,
- scan指令:使?scan指令,scan指令可以?阻塞的提取出指定模式的key串列,但是會有?定的重復概率,在客戶端做?次去重就可以了 ,但是整體所花費的時間會?直接?keys指令長,
- SMEMBERS smembers命令可以回傳集合鍵當前包含的所有元素
- scan增量迭代命令,因為在對鍵的迭代程序中,鍵可能會被修改,所以增量迭代命令只能對回傳的元素提高有限的保證
expire命令設定的過期時間是與電腦設備的時鐘相關的,比如你設定某key的過期時間為1000,但是在1000之內的時間范圍內,你修改了電腦的時間為2000之后,那么此key會立即過期,所以redis的過期時間不是要持續多長時間,而是和電腦時鐘相關聯,
setnx命令
SETNX,是「SET if Not eXists」的縮寫,也就是只有不存在的時候才設定,可以利用它來實作鎖的效果,
SETNX key value
將 key 的值設為 value ,當且僅當 key 不存在,
若給定的 key 已經存在,則 SETNX 不做任何動作,
特殊資料結構
Bitmap :
位圖是?持按 bit 位來存盤資訊,可以?來實作 布隆過濾器(BloomFilter)和 日常打卡:統計用戶資訊,活躍,不活躍! 登錄 、 未登錄! 打卡,365打卡!;
位存盤
Bitmap 位圖,資料結構! 都是操作二進制位來進行記錄,就只有0 和 1 兩個狀態!
HyperLogLog:
提供不精確的去重計數功能,?較適合?來做?規模資料的去重統計,例如統計 UV;
優點:占用的記憶體是固定,2^64 不同的元素的技術,只需要廢 12KB記憶體!如果要從記憶體角度來比較的話 Hyperloglog 首選!
Geospatial:
可以?來保存地理位置,并作位置距離計算或者根據半徑計算位置等,有沒有想過?Redis來實作附近的??或者計算最優地圖路徑?
pub/sub:
功能是訂閱發布功能,可以?作簡單的訊息佇列,
缺點:在消費者下線的情況下,?產的訊息會丟失,得使?專業的訊息佇列如rabbitmq等,
其他用法
Pipeline:
可以批量執??組指令,?次性回傳全部結果,可以減少頻繁的請求應答,可以將多次IO往返的時間縮減為?次,前提是pipeline執?的指令之間沒有因果相關性,使?redisbenchmark進?壓測的時候可以發現影響redis的QPS峰值的?個重要因素是pipeline批次指令的數?,
Lua:
Redis ?持提交 Lua 腳本來執??系列的功能,
事務:
Redis 提供的不是嚴格的事務,Redis 只保證串?執?命令,并且能保證全部執?,但是執?命令失敗時并不會回滾,?是會繼續執?下去,
Redis事務沒有沒有隔離級別的概念!
所有的命令在事務中,并沒有直接被執行!只有發起執行命令的時候才會執行!Exec
Redis單條命令式保存原子性的,但是事務不保證原子性!
Redis事務功能是通過MULTI、EXEC、DISCARD和WATCH 四個原語實作的
Redis會將一個事務中的所有命令序列化,然后按順序執行,
1.redis 不支持回滾“Redis 在事務失敗時不進行回滾,而是繼續執行余下的命令”, 所以 Redis 的內部可以保持簡單且快速,
2.如果在一個事務中的命令出現錯誤,那么所有的命令都不會執行;
3.如果在一個事務中出現運行錯誤,那么正確的命令會被執行,
1)MULTI命令用于開啟一個事務,它總是回傳OK, MULTI執行之后,客戶端可以繼續向服務器發送任意多條命令,這些命令不會立即被執行,而是被放到一個佇列中,當EXEC命令被呼叫時,所有佇列中的命令才會被執行,
2)EXEC:執行所有事務塊內的命令,回傳事務塊內所有命令的回傳值,按命令執行的先后順序排列,當操作被打斷時,回傳空值 nil ,
3)通過呼叫DISCARD,客戶端可以清空事務佇列,并放棄執行事務, 并且客戶端會從事務狀態中退出,
4)WATCH 命令可以為 Redis 事務提供 check-and-set (CAS)行為, 可以監控一個或多個鍵,一旦其中有一個鍵被修改(或洗掉),之后的事務就不會執行,監控一直持續到EXEC命令,
持久化
Redis是一個支持持久化的記憶體資料庫,通過持久化機制把記憶體中的資料同步到硬碟檔案來保證資料持久化,當Redis重啟后通過把硬碟檔案重新加載到記憶體,就能達到恢復資料的目的,
Redis 提供了 RDB 和 AOF 兩種持久化?式,
RDB 是把記憶體中的資料集以快照形式寫?磁盤,實際操作是通過 fork ?行程執?,采??進制壓縮存盤;Redis會單獨創建(fork)一個子行程來進行持久化,會先將資料寫入到一個臨時檔案中,待持久化程序都結束了,再用這個臨時檔案替換上次持久化好的檔案

原理:fork和cow,fork是指redis通過創建?行程來進?RDB操作,cow指的是copy on write,?行程創建后,??行程共享資料段,?行程繼續提供讀寫服務,寫臟的頁?資料會逐漸和?行程分離開來,
AOF 是以?本?志的形式記錄 Redis 處理的每?個寫?或洗掉操作,(讀操作不記錄)
只許追加檔案但不可以改寫檔案
RDB 把整個 Redis 的資料保存在單??件中,?便資料恢復,最?化redis性能,恢復?資料集速度更快,?較適合?來做災備,但缺點是快照保存完成之前如果宕機,這段時間的資料將會丟失,另外保存快照時可能導致服務短時間不可?,
AOF 對?志?件的寫?操作使?的追加模式,有靈活的同步策略,?持每秒同步、每次修改同步和不同步,保存資料更完整,在redis重啟是會重放這些命令來恢復資料,操作效率?,故障丟失資料更少,但是?件體積更?,缺點就是相同規模的資料集,AOF 要?于 RDB,AOF 在運?效率上往往會慢于 RDB,
這兩種機制各?優缺點是啥?
RDB 冷備 是對資料執行周期性的持久化
優點:對性能影響小
1、適合大規模的資料恢復!
2、對資料的完整性要不高!
缺點:快照檔案,默認5分鐘生成一次,那容易丟失5分鐘之內的資料,如果檔案很大,客戶端也可能暫停幾毫秒或者幾秒
1、需要一定的時間間隔行程操作!如果redis意外宕機了,這個最后一次修改資料就沒有的了!
2、fork行程的時候,會占用一定的內容空間
AOF 熱備 對每條寫入命令作為日志,以append-only追加的模式寫入一個日志檔案中
優點:如果一秒一寫,最多丟失一秒資料, 只用追加方式寫資料,少去很多磁盤尋址開銷
1、每一次修改都同步,檔案的完整會更加好!
2、每秒同步一次,可能會丟失一秒的資料
3、從不同步,效率最高的!
缺點:一樣的資料,AOF檔案比RDB檔案要大
1、相對于資料檔案來說,aof遠遠大于 rdb,修復的速度也比 rdb慢!
2、Aof 運行效率也要比 rdb 慢,所以我們redis默認的配置就是rdb持久化
Redis如何做持久化
bgsave做鏡像全量持久化,aof做增量持久化,因為bgsave會耗費較長時間,不夠實時,在停機的時候會導致?量丟失資料 ,所以需要aof來配合使?,在redis實體重啟時,會使?bgsave持久化?件重新構建記憶體,再使?aof重放近期的操作指令來 實 現完整恢復重啟之前的狀態,
這?很好理解,把RDB理解為?整個表全量的資料,AOF理解為每次操作的?志就好了,服務器重啟的時候先把表的資料全部搞進去,但是他可能不完整,你再回放?下?志,資料不就完整了嘛,不過Redis本身的機制是 AOF持久化開啟且存在AOF?件時,優先加載AOF?件;AOF關倍訓者AOF?件不存在時,加載RDB?件;加載AOF/RDB?件城后,Redis啟動成功; AOF/RDB?件存在錯誤時,Redis啟動失敗并列印錯誤資訊
機器斷電對資料丟失的影響
AOF日志sync屬性的配置,如果不要求性能,在每寫一條指令時都sync一下磁盤,就不會丟失資料,但在高性能的要求下,每次都sync是不現實的,一般使用定時sync,比如1s1次,這個時候最多丟失1秒的資料
?可?
Redis ?持主從同步,提供 Cluster 集群部署模式,通過 Sentine l哨兵來監控Redis 主服務器的狀態,當主掛掉時,在從節點中根據?定策略選出新主,并調整其他從 slaveof 到新主,
選主的策略簡單來說有三個:
- slave 的 priority 設定的越低,優先級越?;
- 同等情況下,slave 復制的資料越多優先級越?;
- 相同的條件下 runid 越?越容易被選中,
在 Redis 集群中,sentinel 也會進?多實體部署,sentinel 之間通過 Raft 協議來保證?身的?可?,
Redis Cluster 使?分?機制,在內部分為 16384 個 slot 插槽,分布在所有 master 節點上,每個master 節點負責?部分 slot,資料操作時按 key 做 CRC16 來計算在哪個 slot,由哪個 master 進?處理,資料的冗余是通過 slave 節點來保障,
哨兵
哨兵模式是一種特殊的模式,首先Redis提供了哨兵的命令,哨兵是一個獨立的行程,作為行程,它會獨立運行,其原理是哨兵通過發送命令,等待Redis服務器回應,從而監控運行的多個Redis實體,
能夠后臺監控主機是否故障,如果故障了根據投票數自動將從庫轉換為主庫,
哨兵必須?三個實體去保證??的健壯性的,哨兵+主從并不能保證資料不丟失,但是可以保證集群的?可?,
為啥必須要三個實體呢?我們先看看兩個哨兵會咋樣,
master宕機了 s1和s2兩個哨兵只要有?個認為你宕機了就切換了,并且會選舉出?個哨兵去執?故障,但是這個時候也需要?多數哨兵都是運?的,
那這樣有啥問題呢?M1宕機了,S1沒掛那其實是OK的,但是整個機器都掛了呢?哨兵就只剩下S2一個了,沒有哨兵去允許故障轉移了,雖然另外?個機器上還有R1,但是故障轉移就是不執?,
經典的哨兵集群

M1所在的機器掛了,哨兵還有兩個,兩個看M1他掛了,那我們之中就選舉?個出來執?故障轉移
主要功能:
集群監控:負責監控 Redis master 和 slave 行程是否正常?作,
訊息通知:如果某個 Redis 實體有故障,那么哨兵負責發送訊息作為報警通知給管理員,
故障轉移:如果 master node 掛掉了,會?動轉移到 slave node 上,
配置中?:如果故障轉移發?了,通知 client 客戶端新的 master 地址,
主從
master主寫,資料同步給slave機器,slave主讀,分發掉?量的請求?且輕松實作?平擴容,
Redis的同步機制
(1):全量拷貝
1.slave第?次啟動時,連接Master,發送PSYNC命令,
2.master會執?bgsave命令來?成rdb?件,期間的所有寫命令將被寫?緩沖區,
3. master bgsave執?完畢,向slave發送rdb?件
4. slave收到rdb?件,丟棄所有舊資料,開始載?rdb?件
5. rdb?件同步結束之后,slave執?從master緩沖區發送過來的所以寫命令,
6. 此后 master 每執??個寫命令,就向slave發送相同的寫命令,
7. 主節點根據偏移量把復制積壓緩沖區?的資料發送給從節點,保證主從復制進?正常狀態,
(2):增量拷貝 如果出現?絡閃斷或者命令丟失等例外情況,從節點之前保存了?身已復制的偏移量和主節點的運?ID
redis集群模式性能優化
(1) Master最好不要做任何持久化?作,如RDB記憶體快照和AOF?志?件
(2) 如果資料?較重要,某個Slave開啟AOF備份資料,策略設定為每秒同步?次
(3) 為了主從復制的速度和連接的穩定性,Master和Slave最好在同?個局域?內
(4) 盡量避免在壓?很?的主庫上增加從庫
(5) 主從復制不要?圖狀結構,?單向鏈表結構更為穩定,即:Master <- Slave1 <- Slave2 <-Slave3…這樣的結構?便解決單點故障問題,實作Slave對Master的替換,如果Master掛了,可以?刻啟?Slave1做Master,其他不變,
Redis快取

redis的過期策略以及記憶體淘汰機制:定期+惰性+記憶體淘汰
快取淘汰策略
(1):先進先出演算法(FIFO)
(2):最近使?最少Least Frequently Used(LFU)
(3):最長時間未被使?的Least Recently Used(LRU)
當存在熱點資料時,LRU的效率很好,但偶發性的、周期性的批量操作會導致LRU命中率急劇下降,快取污染情況?較嚴重
redis過期key洗掉策略
(1):惰性洗掉,cpu友好,但是浪費cpu資源
(2):定時洗掉(不常?)
(3):定期洗掉,cpu友好,節省空間
key 失效機制
Redis 的 key 可以設定過期時間,過期后 Redis 采?主動和被動結合的失效機制,?個是和 MC ?樣在訪問時觸發被動洗掉,另?種是定期的主動洗掉,定期+惰性+記憶體淘汰
redis采用的是定期洗掉+惰性洗掉策略,
為什么不用定時洗掉策略?
定時洗掉,用一個定時器來負責監視key,過期則自動洗掉,雖然記憶體及時釋放,但是十分消耗CPU資源,在大并發請求下,CPU要將時間應用在處理請求,而不是洗掉key,因此沒有采用這一策略.
定期洗掉+惰性洗掉是如何作業的呢?
定期洗掉,redis默認每個100ms檢查,是否有過期的key,有過期key則洗掉,需要說明的是,redis不是每個100ms將所有的key檢查一次,而是隨機抽取進行檢查(如果每隔100ms,全部key進行檢查,redis豈不是卡死),因此,如果只采用定期洗掉策略,會導致很多key到時間沒有洗掉,
于是,惰性洗掉派上用場,也就是說在你獲取某個key的時候,redis會檢查一下,這個key如果設定了過期時間那么是否過期了?如果過期了此時就會洗掉,
采用定期洗掉+惰性洗掉就沒其他問題了么?
不是的,如果定期洗掉沒洗掉key,然后你也沒即時去請求key,也就是說惰性洗掉也沒生效,這樣,redis的記憶體會越來越高,那么就應該采用記憶體淘汰機制,
記憶體淘汰策略
volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰
volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰
volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰
allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰
allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰
no-enviction(驅逐):禁止驅逐資料,新寫入操作會報錯
ps:如果沒有設定 expire 的key, 不滿足先決條件(prerequisites); 那么 volatile-lru, volatile-random 和volatile-ttl 策略的行為, 和 noeviction(不洗掉) 基本上一致,
手寫LRU代碼
leetcode LRU
linkedhashmap HashMap和雙向鏈表
class LRUCache {
private int capacity;
private LinkedHashMap<Integer, Integer> map;
public LRUCache(int capacity) {
this.capacity = capacity;
map = new LinkedHashMap<>();
}
public int get(int key) {
if(map.containsKey(key)){
int value = map.get(key);
map.remove(key);
map.put(key, value);
return value;
}else{
return -1;
}
}
public void put(int key, int value) {
if(map.containsKey(key)){
map.remove(key);
}
if(map.size() >= capacity){
map.remove(map.keySet().iterator().next());
}
map.put(key, value);
}
}
常見快取問題

快取更新?式
這是決定在使?快取時就該考慮的問題,
快取的資料在資料源發?變更時需要對快取進?更新,資料源可能是 DB,也可能是遠程服務,更新的?式可以是主動更新,資料源是 DB 時,可以在更新完 DB 后就直接更新快取,
當資料源不是 DB ?是其他遠程服務,可能?法及時主動感知資料變更,這種情況下?般會選擇對快取資料設定失效期,也就是資料不?致的最?容忍時間,
這種場景下,可以選擇失效更新,key 不存在或失效時先請求資料源獲取最新資料,然后再次快取,并更新失效期,
但這樣做有個問題,如果依賴的遠程服務在更新時出現例外,則會導致資料不可?,改進的辦法是異步更新,就是當失效時先不清除資料,繼續使?舊的資料,然后由異步執行緒去執?更新任務,這樣就避免了失效瞬間的空窗期,另外還有?種純異步更新?式,定時對資料進?分批更新,實際使?時可以根據業務場景選擇更新?式,
資料不?致
第?個問題是資料不?致的問題,可以說只要使?快取,就要考慮如何?對這個問題,快取不?致產?的原因?般是主動更新失敗,例如更新 DB 后,更新 Redis 因為?絡原因請求超時;或者是異步更新失敗導致,
解決的辦法是,如果服務對耗時不是特別敏感可以增加重試;如果服務對耗時敏感可以通過異步補償任務來處理失敗的更新,或者短期的資料不?致不會影響業務,那么只要下次更新時可以成功,能保證最終?致性就可以,
如何解決?致性問題?
延遲雙刪
?般來說,如果允許快取可以稍微的跟資料庫偶爾有不?致的情況,也就是說如果你的系統不是嚴格要求 “快取+資料庫” 必須保持?致性的話,最好不要做這個?案,即:讀請求和寫請求串?化,串到?個記憶體佇列?去,
串?化可以保證?定不會出現不?致的情況,但是它也會導致系統的吞吐量?幅度降低,??正常情況下多?倍的機器去?撐線上的?個請求,把?些列的操作都放到佇列??,順序肯定不會亂,但是并發?了,這佇列很容易阻塞,反?會成為整個系統的弱點,瓶頸
快取穿透(查不到)
快取穿透,產?這個問題的原因可能是外部的惡意攻擊,例如,對?戶資訊進?了快取,但惡意攻擊者使?不存在的?戶id頻繁請求接?,導致查詢快取不命中,然后穿透 DB 查詢依然不命中,這時會有?量請求穿透快取訪問到 DB,
解決的辦法如下,
- 接?層增加校驗,?如?戶鑒權校驗,引數做校驗,不合法的引數直接代碼Return,?如:id 做基礎校驗,id <=0的直接攔截等,
- 快取空物件:對不存在的?戶,在快取中保存?個空物件進?標記,防?相同 ID 再次訪問 DB,不過有時這個?法并不能很好解決問題,可能導致快取中存盤?量??資料,
- 布隆過濾器:使? BloomFilter 過濾器,BloomFilter 的特點是存在性檢測,如果 BloomFilter 中不存在,那么資料?定不存在;如果 BloomFilter 中存在,實際資料也有可能會不存在,?常適合解決這類的問題,


快取擊穿(量太大,快取過期!)
快取擊穿,就是某個熱點資料失效時,?量針對這個資料的請求會穿透到資料源,
解決這個問題有如下辦法,
- 設定熱點資料永不過期:從快取層面來看,沒有設定過期時間,所以不會出現熱點 key 過期后產生的問題,
- 加互斥鎖:可以使?互斥鎖更新,保證同?個行程中針對同?個資料不會并發請求到 DB,減? DB 壓?,
- 使?隨機退避?式,失效時隨機 sleep ?個很短的時間,再次查詢,如果失敗再執?更新,
- 針對多個熱點 key 同時失效的問題,可以在快取時使?固定時間加上?個?的亂數,避免?量熱點 key 同?時刻失效,
快取雪崩
快取雪崩,是指在某一個時間段,快取集中過期失效,Redis 宕機,產?的原因是快取掛掉,這時所有的請求都會穿透到 DB,
解決?法:
6. 限流降級:使?快速失敗的熔斷策略,減少 DB 瞬間壓?;
7. ?可?:使?主從模式和集群模式來盡量保證快取服務的?可?,
8. 資料預熱:資料加熱的含義就是在正式部署之前,我先把可能的資料先預先訪問一遍,這樣部分可能大量訪問的資料就會加載到快取中,在即將發生大并發訪問前手動觸發加載快取不同的key,設定不同的過期時間,讓快取失效的時間點盡量均勻,
9. 快取失效時間分散開,
快取預熱
快取預熱這個應該是一個比較常見的概念,相信很多小伙伴都應該可以很容易的理解,快取預熱就是系統上線后,將相關的快取資料直接加載到快取系統,這樣就可以避免在用戶請求的時候,先查詢資料庫,然后再將資料快取的問題!用戶直接查詢事先被預熱的快取資料!
解決思路:
1、直接寫個快取重繪頁面,上線時手工操作下;
2、資料量不大,可以在專案啟動的時候自動進行加載;
3、定時重繪快取;
快取更新
除了快取服務器自帶的快取失效策略之外(Redis默認的有6中策略可供選擇),我們還可以根據具體的業務需求進行自定義的快取淘汰,常見的策略有兩種:
(1)定時去清理過期的快取;
(2)當有用戶請求過來時,再判斷這個請求所用到的快取是否過期,過期的話就去底層系統得到新資料并更新快取,
兩者各有優劣,第一種的缺點是維護大量快取的key是比較麻煩的,第二種的缺點就是每次用戶請求過來都要判斷快取失效,邏輯相對比較復雜!具體用哪種方案,大家可以根據自己的應用場景來權衡,
快取降級
當訪問量劇增、服務出現問題(如回應時間慢或不回應)或非核心服務影響到核心流程的性能時,仍然需要保證服務還是可用的,即使是有損服務,系統可以根據一些關鍵資料進行自動降級,也可以配置開關實作人工降級,
降級的最終目的是保證核心服務可用,即使是有損的,而且有些服務是無法降級的(如加入購物車、結算),
以參考日志級別設定預案:
(1)一般:比如有些服務偶爾因為網路抖動或者服務正在上線而超時,可以自動降級;
(2)警告:有些服務在一段時間內成功率有波動(如在95~100%之間),可以自動降級或人工降級,并發送告警;
(3)錯誤:比如可用率低于90%,或者資料庫連接池被打爆了,或者訪問量突然猛增到系統能承受的最大閥值,此時可以根據情況自動降級或者人工降級;
(4)嚴重錯誤:比如因為特殊原因資料錯誤了,此時需要緊急人工降級,
服務降級的目的,是為了防止Redis服務故障,導致資料庫跟著一起發生雪崩問題,因此,對于不重要的快取資料,可以采取服務降級策略,例如一個比較常見的做法就是,Redis出現問題,不去資料庫查詢,而是直接回傳默認值給用戶,
熱點資料和冷資料是什么
熱點資料,快取才有價值
對于冷資料而言,大部分資料可能還沒有再次訪問到就已經被擠出記憶體,不僅占用記憶體,而且價值不大,頻繁修改的資料,看情況考慮使用快取
對于上面兩個例子,壽星串列、導航資訊都存在一個特點,就是資訊修改頻率不高,讀取通常非常高的場景,
對于熱點資料,比如我們的某IM產品,生日祝福模塊,當天的壽星串列,快取以后可能讀取數十萬次,再舉個例子,某導航產品,我們將導航資訊,快取以后可能讀取數百萬次,
資料更新前至少讀取兩次,快取才有意義,這個是最基本的策略,如果快取還沒有起作用就失效了,那就沒有太大價值了,
那存不存在,修改頻率很高,但是又不得不考慮快取的場景呢?有!比如,這個讀取介面對資料庫的壓力很大,但是又是熱點資料,這個時候就需要考慮通過快取手段,減少資料庫的壓力,比如我們的某助手產品的,點贊數,收藏數,分享數等是非常典型的熱點資料,但是又不斷變化,此時就需要將資料同步保存到Redis快取,減少資料庫壓力,
hot key出現造成集群訪問量傾斜解決辦法
(1):使?本地快取
(2): 利?分?演算法的特性,對key進?打散處理(給hot key加上前綴或者后綴,把?個hotkey 的數量變成 redis 實體個數N的倍數M,從?由訪問?個 redis key 變成訪問 N * M 個redis key)
如果有?量的key需要設定同?時間過期,?般需要注意什么?
如果?量的key過期時間設定的過于集中,到過期的那個時間點,redis可能會出現短暫的卡頓現象,嚴重的話會出現快取雪崩,我們?般需要在時間上加?個隨機值,使得過期時間分散?些,
最經典的KV、DB讀寫模式么?
最經典的快取+資料庫讀寫的模式,就是 Cache Aside Pattern
- 讀的時候,先讀快取,快取沒有的話,就讀資料庫,然后取出資料后放?快取,同時回傳回應,
- 更新的時候,先更新資料庫,然后再洗掉快取,
為什么是洗掉快取,?不是更新快取?
原因很簡單,很多時候,在復雜點的快取場景,快取不單單是資料庫中直接取出來的值,
?如可能更新了某個表的?個欄位,然后其對應的快取,是需要查詢另外兩個表的資料并進?運算,才能計算出快取最新的值的,
另外更新快取的代價有時候是很?的,是不是說,每次修改資料庫的時候,都?定要將其對應的快取更新?份?也許有的場景是這樣,但是對于?較復雜的快取資料計算的場景,就不是這樣了,如果你頻繁修改?個快取涉及的多個表,快取也頻繁更新,但是問題在于,這個快取到底會不會被頻繁訪問到?
舉個栗?:?個快取涉及的表的欄位,在 1 分鐘內就修改了 20 次,或者是 100 次,那么快取更新 20次、100 次;但是這個快取在 1 分鐘內只被讀取了 1 次,有?量的冷資料,
實際上,如果你只是洗掉快取的話,那么在 1 分鐘內,這個快取不過就重新計算?次?已,開銷?幅度降低,?到快取才去算快取,
其實洗掉快取,?不是更新快取,就是?個 Lazy 計算的思想,不要每次都重新做復雜的計算,不管它會不會?到,?是讓它到需要被使?的時候再重新計算,
像 Mybatis,Hibernate,都有懶加載思想,查詢?個部門,部門帶了?個員?的 List,沒有必要說每次查詢部門,都??的 1000 個員?的資料也同時查出來啊,80% 的情況,查這個部門,就只是要訪問這個部門的資訊就可以了,先查部門,同時要訪問??的員?,那么這個時候只有在你要訪問??的員?的時候,才會去資料庫??查詢 1000 個員?,
與memcache的區別
redis相比memcache來說,擁有更多的資料結構,支持更豐富的資料操作,
支持集群模式,memcache沒有原生的集群模式,依靠客戶端集群分片
redis單核,memcache多核,在存盤小資料的時候,redis性能更高
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/69849.html
標籤:AI
