Redis寶典

什麼是Redis

Redis 是一個開源(BSD許可)的,內存中的數據結構存儲系統,它可以用作數據庫、緩存和消息中間件。 它支持多種類型的數據結構,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 與範圍查詢, bitmaps, hyperloglogs 和 地理空間(geospatial) 索引半徑查詢。 Redis 內置了 複製(replication),LUA腳本(Lua scripting), LRU驅動事件(LRU eviction),事務(transactions) 和不同級別的 磁盤持久化(persistence), 並通過 Redis哨兵(Sentinel)和自動 分區(Cluster)提供高可用性(high availability)。

Redis的優點

  1. 豐富的數據類型(string、hash、list、set、sorted set)
  2. 支持持久化(AOF、RDB)
  3. Redis支持數據的備份,即master-slave模式的數據備份
  4. 讀寫速度快. 數據存放在內存中,數據結構類似於HashMap,HashMap的優勢就是查找和操作的時間複雜度都是O(1) – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
  5. Memcached是多線程,非阻塞IO複用的網絡模型;Redis使用單線程的IO複用模型
  6. 支持事務,而且操作都是原子性
  7. 其他:publish/subscribe, 通知, key 過期等等特性

Redis 事務可以一次執行多個命令, 並且帶有以下三個重要的保證:

批量操作在發送 EXEC 命令前被放入隊列緩存。
收到 EXEC 命令後進入事務執行,事務中任意命令執行失敗,其餘的命令依然被執行。
在事務執行過程,其他客戶端提交的命令請求不會插入到事務執行命令序列中。

一個事務從開始到執行會經歷以下三個階段:
開始事務。
命令入隊。
執行事務。

在Redis事務發生錯誤解決辦法
redis事務錯誤分兩種,
(1)在事務開始之前,命令可能排隊失敗。如,命令的語法可能是錯誤 (錯誤的參數個數,錯誤的命令名字 …),或一些重要的環境問題,如,內存不足。
解決辦法:在命令排隊期間發生錯誤,Redis會拒絕執行 EXEC,並返回一個錯誤,然後自動放棄這個事務。
(2)在事務執行中。當EXEC 調用後 ,一些命令可能執行失敗,如,在一個字符串上進行了列表命令的操作。
解決辦法:redis會跳過發生錯誤的命令,繼續接下來的命令。
注:Redis沒有回滾機制

redis和memcached的區別
(1) memcached所有的值均是簡單的字符串,redis支持更爲豐富的數據類型

(2)由於Redis只使用單核,而Memcached可以使用多核,所以平均每一個核上Redis在存儲小數據時比Memcached性能更高。而在100k以上的數據時,Memcached性能要高於Redis,雖然Redis最近也在存儲大數據的性能上進行優化,但是比起Memcached,還是稍有遜色。

(3) 雖然redis和Memcached都是內存數據庫,但是redis可以持久化其數據,Memcached不支持持久化. (Redis並不是所有的數據都一直存儲在內存中的,當物理內存用完時,Redis可以將一些很久沒用到的value交換到磁盤,但memcached超過內存比例會抹掉前面的數據。)所以memcached掛掉後(比如說斷電,重啓系統等等),數據不可恢復;redis數據丟失後可以通過AOF恢復

(4)分佈式存儲

Memcached是全內存的數據緩衝系統,Redis雖然支持數據的持久化,但是全內存畢竟纔是其高性能的本質。作爲基於內存的存儲系統來說,機器物理內存的大小就是系統能夠容納的最大數據量。如果需要處理的數據量超過了單臺機器的物理內存大小,就需要構建分佈式集羣來擴展存儲能力。

Memcached本身並不支持分佈式,因此只能在客戶端通過像一致性哈希這樣的分佈式算法來實現Memcached的分佈式存儲,關於分佈式一致性哈希算法見總結:分佈式一致性hash算法。當客戶端向Memcached集羣發送數據之前,首先會通過內置的分佈式算法計算出該條數據的目標節點,然後數據會直接發送到該節點上存儲。但客戶端查詢數據時,同樣要計算出查詢數據所在的節點,然後直接向該節點發送查詢請求以獲取數據。

相較於Memcached只能採用客戶端實現分佈式存儲,Redis更偏向於在服務器端構建分佈式存儲,但沒有采用一致性哈希,關於Redis集羣分析見總結:分佈式緩存Redis之cluster集羣。最新版本的Redis已經支持了分佈式存儲功能。Redis Cluster是一個實現了分佈式且允許單點故障的Redis高級版本,它沒有中心節點,具有線性可伸縮的功能。爲了保證單點故障下的數據可用性,Redis Cluster引入了Master節點和Slave節點。在Redis Cluster中,每個Master節點都會有對應的兩個用於冗餘的Slave節點。這樣在整個集羣中,任意兩個節點的宕機都不會導致數據的不可用。當Master節點退出後,集羣會自動選擇一個Slave節點成爲新的Master節點。

redis支持master-slave複製模式

memcache可以使用一致性hash做分佈式

如果有持久化方面的需求或者對數據類型和處理有要求的應該選擇redis;如果是簡單的key/value存儲可以考慮memcached
內存管理機制
Memcached主要的cache機制是LRU(最近最少使用Least Recently Used)算法+超時失效。

Redis採用的是包裝的mallc/free,相較於Memcached的內存管理方法來說,要簡單很多。

redis的單線程爲什麼那麼快
redis分客戶端和服務端,一次完整的redis請求事件有多個階段(客戶端到服務器的網絡連接–>redis讀寫事件發生–>redis服務端的數據處理(單線程)–>數據返回)。平時所說的redis單線程模型,本質上指的是服務端的數據處理

客戶端和服務器是socket通信方式,socket服務端監聽可同時接受多個客戶端請求也就是說,redis服務同時面對多個redis客戶端連接請求,而redis服務本身是單線程運行。

redis 核心就是 如果我的數據全都在內存裏,我單線程的去操作 就是效率最高的,爲什麼呢,因爲多線程的本質就是 CPU 模擬出來多個線程的情況,這種模擬出來的情況就有一個代價,就是上下文的切換,對於一個內存的系統來說,它沒有上下文的切換就是效率最高的。redis 用 單個CPU 綁定一塊內存的數據,然後針對這塊內存的數據進行多次讀寫的時候,都是在一個CPU上完成的,所以它是單線程處理這個事。在內存的情況下,這個方案就是最佳方案

使用單線程的方式是無法發揮多核CPU 性能, 爲了充分利用多核CPU,常常在一臺server上會啓動多個實例(即多個redis進程)。而爲了減少切換的開銷,有必要爲每個實例(redis進程)指定其所運行的CPU

而且因爲redis是單線程的,所以不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因爲可能出現死鎖而導致的性能消耗;

總結:CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機器內存的大小或者網絡帶寬。既然單線程容易實現,而且CPU不會成爲瓶頸,那就順理成章地採用單線程的方案了(畢竟採用多線程會有很多麻煩!)

redis的主從結構
主從結構一是可以進行冗餘備份,二是可以實現讀寫分離

主從複製
冗餘備份(還可以稱爲:主從複製,數據冗餘,數據備份,可以實現容災快速恢復)

持久化保證了即使redis服務重啓也會丟失數據,因爲redis服務重啓後會將硬盤上持久化的數據恢復到內存中,但是當redis服務器的硬盤損壞了可能會導致數據丟失,如果通過redis的主從複製機制就可以避免這種單點故障. 例如:我們搭建一個主叫做redis0,兩個從,分別叫做redis1和redis2,即使一臺redis服務器宕機其它兩臺redis服務也可以繼續提供服務。主redis中的數據和從redis上的數據保持實時同步,當主redis寫入數據時通過主從複製機制會複製到兩個從redis服務上。

①一個Master可以有多個Slave,不僅主服務器可以有從服務器,從服務器也可以有自己的從服務器

②複製在Master端是非阻塞模式的,這意味着即便是多個Slave執行首次同步時,Master依然可以提供查詢服務;

③複製在Slave端也是非阻塞模式的:如果你在redis.conf做了設置,Slave在執行首次同步的時候仍可以使用舊數據集提供查詢;你也可以配置爲當Master與Slave失去聯繫時,讓Slave返回客戶端一個錯誤提示;

④當Slave要刪掉舊的數據集,並重新加載新版數據時,Slave會阻塞連接請求

讀寫分離
主從架構中,可以考慮關閉主服務器的數據持久化功能,只讓從服務器進行持久化,這樣可以提高主服務器的處理性能。從服務器通常被設置爲只讀模式,這樣可以避免從服務器的數據被誤修改。

基於Redis分佈式鎖原理
分佈式鎖一般有三種實現方式:1. 數據庫樂觀鎖;2. 基於Redis的分佈式鎖;3. 基於ZooKeeper的分佈式鎖。
先拿setnx來爭搶鎖,搶到之後,再用expire給鎖加一個過期時間防止鎖忘記了釋放。
這時候對方會告訴你說你回答得不錯,然後接着問如果在setnx之後執行expire之前進程意外crash或者要重啓維護了,那會怎麼樣?
這時候你要給予驚訝的反饋:唉,是喔,這個鎖就永遠得不到釋放了。緊接着你需要抓一抓自己得腦袋,故作思考片刻,好像接下來的結果是你主動思考出來的,然後回答:我記得set指令有非常複雜的參數(如下),這個應該是可以同時把setnx和expire合成一條指令來用的!對方這時會顯露笑容,心裏開始默唸:嗯,這小子還不錯。

SET resource-name anystring NX EX max-lock-time

如果上面的命令返回,客戶端可以獲取鎖OK(如果命令返回Nil,則在一段時間後重試),並使用DEL刪除鎖。
到達過期時間後,鎖定將自動釋放。

可以使此係統更加健壯,修改解鎖模式如下:
不要設置固定字符串,而是設置一個名爲token的不可猜測的大型隨機字符串。
不是使用DEL釋放鎖,而是發送一個腳本,該腳本僅在值匹配時才刪除密鑰。
這避免了客戶端在過期時間之後嘗試釋放鎖定,從而刪除由稍後獲取鎖定的另一個客戶端創建的密鑰。

Redis裏面有1億個key,其中有10w個key是以某個固定的已知的前綴開頭的,如何將它們全部找出來
使用keys指令可以掃出指定模式的key列表。
對方接着追問:如果這個redis正在給線上的業務提供服務,那使用keys指令會有什麼問題?
這個時候你要回答redis關鍵的一個特性:redis的單線程的。keys指令會導致線程阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重複概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。

如何使用Redis做異步隊列
一般使用list結構作爲隊列,rpush生產消息,lpop消費消息。當lpop沒有消息的時候,要適當sleep一會再重試。

如果對方追問可不可以不用sleep呢?
list還有個指令叫blpop,在沒有消息的時候,它會阻塞住直到消息到來。

如果對方追問能不能生產一次消費多次呢?
使用pub/sub主題訂閱者模式,可以實現1:N的消息隊列。

如果對方追問pub/sub有什麼缺點?
在消費者下線的情況下,生產的消息會丟失,得使用專業的消息隊列如rabbitmq等。

如果對方追問redis如何實現延時隊列?
我估計現在你很想把面試官一棒打死如果你手上有一根棒球棍的話,怎麼問的這麼詳細。但是你很剋制,然後神態自若的回答道:使用sortedset,拿時間戳作爲score,消息內容作爲key調用zadd來生產消息,消費者用zrangebyscore指令獲取N秒之前的數據輪詢進行處理。
到這裏,面試官暗地裏已經對你豎起了大拇指。但是他不知道的是此刻你卻豎起了中指,在椅子背後。

如果有大量的key需要設置同一時間過期,一般需要注意什麼
如果大量的key過期時間設置的過於集中,到過期的那個時間點,redis可能會出現短暫的卡頓現象。一般需要在時間上加一個隨機值,使得過期時間分散一些。

Redis如何做持久化的?
bgsave做鏡像全量持久化,aof做增量持久化。因爲bgsave會耗費較長時間,不夠實時,在停機的時候會導致大量丟失數據,所以需要aof來配合使用。在redis實例重啓時,優先使用aof來恢復內存的狀態,如果沒有aof日誌,就會使用rdb文件來恢復。
如果再問aof文件過大恢復時間過長怎麼辦?你告訴面試官,Redis會定期做aof重寫,壓縮aof文件日誌大小。如果面試官不夠滿意,再拿出殺手鐗答案,Redis4.0之後有了混合持久化的功能,將bgsave的全量和aof的增量做了融合處理,這樣既保證了恢復的效率又兼顧了數據的安全性。這個功能甚至很多面試官都不知道,他們肯定會對你刮目相看。
如果對方追問那如果突然機器掉電會怎樣?取決於aof日誌sync屬性的配置,如果不要求性能,在每條寫指令時都sync一下磁盤,就不會丟失數據。但是在高性能的要求下每次都sync是不現實的,一般都使用定時sync,比如1s1次,這個時候最多就會丟失1s的數據。

Redis的同步機制
從從同步。第一次同步時,主節點做一次bgsave,並同時將後續修改操作記錄到內存buffer,待完成後將rdb文件全量同步到複製節點,複製節點接受完成後將rdb鏡像加載到內存。加載完成後,再通知主節點將期間修改的操作記錄同步到複製節點進行重放就完成了同步過程。

Redis主要消耗什麼物理資源?

內存。

Redis的全稱是什麼?

Remote Dictionary Server。

Redis官方爲什麼不提供Windows版本?

因爲目前Linux版本已經相當穩定,而且用戶量很大,無需開發windows版本,反而會帶來兼容性等問題。

一個字符串類型的值能存儲最大容量是多少?

512M

爲什麼Redis需要把所有數據放到內存中?

一個字 “快” ,內存存取速度遠超於磁盤I/O

MySQL裏有2000w數據,redis中只存20w的數據,如何保證redis中的數據都是熱點數據?

redis內存數據集大小上升到一定大小的時候,就會施行數據淘汰策略

Redis的數據淘汰策略

redis 提供 6種數據淘汰策略通過maxmemory-policy設置策略:

  • 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(驅逐):禁止驅逐數據

redis默認不採用淘汰策略(no-enviction),直接返回錯誤。

Redis常見數據結構使用場景

  1. String
    常用命令: set,get,decr,incr,mget 等。
    String數據結構是簡單的key-value類型,value其實不僅可以是String,也可以是數字。
    常規key-value緩存應用;
    常用場景:常規計數:微博數,粉絲數等。

  2. Hash
    常用命令: hget,hset,hgetall 等。
    常用場景:存儲部分變更數據,如網站首頁緩存、用戶對象信息等
    我們簡單舉個實例來描述下Hash的應用場景,比如我們要存儲一個用戶對象數據,包含以下信息:

用戶ID爲查找的key,存儲的value用戶對象包含姓名,年齡,生日等信息,如果用普通的key/value結構來存儲,主要有以下2種存儲方式:
在這裏插入圖片描述
第一種方式(如上圖)將用戶ID作爲查找key,把其他信息封裝成一個對象以序列化的方式存儲,這種方式的缺點是,增加了序列化/反序列化的開銷,並且在需要修改其中一項信息時,需要把整個對象取回,並且修改操作需要對併發進行保護,引入CAS等複雜問題。

在這裏插入圖片描述
第二種方法(如上圖)是這個用戶信息對象有多少成員就存成多少個key-value對兒,用用戶ID+對應屬性的名稱作爲唯一標識來取得對應屬性的值,雖然省去了序列化開銷和併發問題,但是用戶ID爲重複存儲,如果存在大量這樣的數據,內存浪費還是非常可觀的。

那麼Redis提供的Hash很好的解決了這個問題,Redis的Hash實際是內部存儲的Value爲一個HashMap,並提供了直接存取這個Map成員的接口,如下圖:
在這裏插入圖片描述
也就是說,Key仍然是用戶ID, value是一個Map,這個Map的key是成員的屬性名,value是屬性值,這樣對數據的修改和存取都可以直接通過其內部Map的Key(Redis裏稱內部Map的key爲field), 也就是通過 key(用戶ID) + field(屬性標籤) 就可以操作對應屬性數據了,既不需要重複存儲數據,也不會帶來序列化和併發修改控制的問題。很好的解決了問題。

這裏同時需要注意,Redis提供了接口(hgetall)可以直接取到全部的屬性數據,但是如果內部Map的成員很多,那麼涉及到遍歷整個內部Map的操作,由於Redis單線程模型的緣故,這個遍歷操作可能會比較耗時,而另其它客戶端的請求完全不響應,這點需要格外注意。

Hash實現方式:

上面已經說到Redis Hash對應Value內部實際就是一個HashMap,實際這裏會有2種不同實現,這個Hash的成員比較少時Redis爲了節省內存會採用類似一維數組的方式來緊湊存儲,而不會採用真正的HashMap結構,對應的value redisObject的encoding爲zipmap,當成員數量增大時會自動轉成真正的HashMap,此時encoding爲ht。

  1. List
    常用命令: lpush,rpush,lpop,rpop,lrange等
    使用場景:最新消息排行、消息隊列等

  2. Set
    常用命令: sadd,spop,smembers,sunion 等
    使用場景:
    set對外提供的功能與list類似是一個列表的功能,特殊之處在於set是可以自動排重的。
    (1)在微博應用中,可以將一個用戶所有的關注人存在一個集合中,將其所有粉絲存在一個集合。Redis可以非常方便的實現如共同關注、共同喜好、二度好友等功能。

    (2)點贊點踩
    當前用戶點讚的話,就將當前用戶id存入到對應點贊集合當中,同時判斷點反對集合中是否有此id值,有的話就移除;
    當前用戶點反對的話,與上面操作相反。
    頁面顯示的時候就根據當前用戶id在點贊集合和反對集合中查找,若id值在點贊集合中有對應值,就顯示1,表示當前用戶點贊;若在反對集合中有值,反對處就顯示1

  3. Sorted Set
    常用命令: zadd,zrange,zrem,zcard等
    和Set相比,Sorted Set增加了一個權重參數score,使得集合中的元素能夠按score進行有序排列並且是插入有序的,即自動排序。
    使用場景:
    (1)比如一個存儲全班同學成績的Sorted Set,其集合value可以是同學的學號,而score就可以是其考試得分,這樣在數據插入集合的時候,就已經進行了天然的排序。
    (2)用Sorted Set來做帶權重的隊列,比如普通消息的score爲1,重要消息的score爲2,然後工作線程可以選擇按score的倒序來獲取工作任務。讓重要的任務優先執行。

說說Redis哈希槽的概念?

Redis集羣沒有使用一致性hash,而是引入了哈希槽的概念,Redis集羣有16384個哈希槽,每個key通過CRC16校驗後對16384取模來決定放置哪個槽,集羣的每個節點負責一部分hash槽。

Redis常見性能問題和解決方案

  1. Master最好不要做任何持久化工作,如RDB內存快照和AOF日誌文件
  2. 如果數據比較重要,某個Slave開啓AOF備份數據,策略設置爲每秒同步一次
  3. 爲了主從複製的速度和連接的穩定性,Master和Slave最好在同一個局域網內
  4. 儘量避免在壓力很大的主庫上增加從庫

Redis集羣會有寫操作丟失嗎?爲什麼?

Redis並不能保證數據的強一致性,這意味這在實際中集羣在特定的條件下可能會丟失寫操作。

Redis集羣之間是如何複製的?

異步複製

Redis集羣最大節點個數是多少?

16384個。因爲Redis集羣有16384個哈希槽

Redis中的管道有什麼用?

一次請求/響應服務器能實現處理新的請求即使舊的請求還未被響應。這樣就可以將多個命令發送到服務器,而不用等待回覆,最後在一個步驟中讀取該答覆。

這就是管道(pipelining),是一種幾十年來廣泛使用的技術。例如許多POP3協議已經實現支持這個功能,大大加快了從服務器下載新郵件的過程。

Redis事務相關的命令有哪幾個?

MULTI、EXEC、DISCARD、WATCH

Redis key的過期時間和永久有效分別怎麼設置?

EXPIRE和PERSIST命令。

Redis如何做內存優化

儘可能使用散列表(hashes),散列表(是說散列表裏面存儲的數少)使用的內存非常小,所以你應該儘可能的將你的數據模型抽象到一個散列表裏面。

比如你的web系統中有一個用戶對象,不要爲這個用戶的名稱,姓氏,郵箱,密碼設置單獨的key,而是應該把這個用戶的所有信息存儲到一張散列表裏面。

Redis回收進程如何工作

一個客戶端運行了新的命令,添加了新的數據。

Redi檢查內存使用情況,如果大於maxmemory的限制, 則根據設定好的策略進行回收。

一個新的命令被執行,等等。

所以我們不斷地穿越內存限制的邊界,通過不斷達到邊界然後不斷地回收回到邊界以下。
如果一個命令的結果導致大量內存被使用(例如很大的集合的交集保存到一個新的鍵),不用多久內存限制就會被這個內存使用量超越。

解決redis主從結構宕機
a)這個相對而言比較簡單,在Redis中從庫重新啓動後會自動加入到主從架構中,自動完成同步數據

b) 如果從庫在斷開期間,主庫的變化不大,從庫再次啓動後,主庫依然會將所有的數據做RDB操作嗎?還是增量更新?(從庫有做持久化的前提下)

不會的,因爲在Redis2.8版本後就實現了,主從斷線後恢復的情況下實現增量複製。

主Redis宕機

手動恢復

i. 第一步,在從數據庫中執行SLAVEOFNO ONE命令,斷開主從關係並且將從庫提升爲主庫繼續服務;

ii.第二步,將主庫重新啓動後,執行SLAVEOF命令,將其設置爲其他庫的從庫,這時數據就能更新回來;

哨兵功能自動恢復

通過sentinel模式啓動redis後,自動監控master/slave的運行狀態, 已經被集成在redis2.4+的版本中

如果Master異常,則會進行Master-Slave切換,將其中一個Slave作爲Master,將之前的Master作爲Slave

基本原理是:心跳機制+投票裁決

每個sentinel會向其它sentinal、master、slave定時發送消息,以確認對方是否“活”着,如果發現對方在指定時間(可配置)內未迴應,則暫時認爲對方已掛(所謂的“主觀認爲宕機” Subjective Down,簡稱SDOWN)。

若”哨兵羣”中的多數sentinel,都報告某一master沒響應,系統才認爲該master”徹底死亡”(即:客觀上的真正down機,Objective Down,簡稱ODOWN),通過一定的vote算法,從剩下的slave節點中,選一臺提升爲master,然後自動修改相關配置。

緩存穿透
緩存穿透是指查詢一個一定不存在的數據,由於緩存是不命中時需要從數據庫查詢,查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到數據庫去查詢,造成緩存穿透。

解決辦法

①對所有可能查詢的參數以hash形式存儲,在控制層先進行校驗,不符合則丟棄。還有最常見的則是採用布隆過濾器,將所有可能存在的數據哈希到一個足夠大的bitmap中,一個一定不存在的數據會被這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。

②也可以採用一個更爲簡單粗暴的方法,如果一個查詢返回的數據爲空(不管是數 據不存在,還是系統故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。

緩存雪崩
如果緩存集中在一段時間內失效,發生大量的緩存穿透,所有的查詢都落在數據庫上,造成了緩存雪崩。

解決辦法

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

②可以通過緩存reload機制,預先去更新緩存,再即將發生大併發訪問前手動觸發加載緩存

③不同的key,設置不同的過期時間,讓緩存失效的時間點儘量均勻. 比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個緩存的過期時間的重複率就會降低,就很難引發集體失效的事件

④做二級緩存,或者雙緩存策略。A1爲原始緩存,A2爲拷貝緩存,A1失效時,可以訪問A2,A1緩存失效時間設置爲短期,A2設置爲長期。

緩存擊穿
緩存被“擊穿”的問題,這個和緩存雪崩的區別在於這裏針對某一key緩存,前者則是很多key。

緩存預熱
緩存預熱就是系統上線後,提前將相關的緩存數據直接加載到緩存系統。避免在用戶請求的時候,先查詢數據庫,然後再將數據緩存的問題!用戶直接查詢事先被預熱的緩存數據!

緩存預熱解決方案:

(1)直接寫個緩存刷新頁面,上線時手工操作下;

(2)數據量不大,可以在項目啓動的時候自動進行加載;

(3)定時刷新緩存;

緩存更新
我們知道通過expire來設置key 的過期時間,那麼對過期的數據怎麼處理呢?

除了緩存服務器自帶的緩存失效策略之外(Redis默認的有6中策略可供選擇),我們還可以根據具體的業務需求進行自定義的緩存淘汰,常見的策略有兩種:

(1)定時去清理過期的緩存;

(2)當有用戶請求過來時,再判斷這個請求所用到的緩存是否過期,過期的話就去底層系統得到新數據並更新緩存。

兩者各有優劣,第一種的缺點是維護大量緩存的key是比較麻煩的,第二種的缺點就是每次用戶請求過來都要判斷緩存失效,邏輯相對比較複雜!具體用哪種方案,大家可以根據自己的應用場景來權衡。

緩存降級
當訪問量劇增、服務出現問題(如響應時間慢或不響應)或非核心服務影響到核心流程的性能時,仍然需要保證服務還是可用的,即使是有損服務。系統可以根據一些關鍵數據進行自動降級,也可以配置開關實現人工降級。

降級的最終目的是保證核心服務可用,即使是有損的。而且有些服務是無法降級的(如加入購物車、結算)。

在進行降級之前要對系統進行梳理,看看系統是不是可以丟卒保帥;從而梳理出哪些必須誓死保護,哪些可降級;比如可以參考日誌級別設置預案:

(1)一般:比如有些服務偶爾因爲網絡抖動或者服務正在上線而超時,可以自動降級;

(2)警告:有些服務在一段時間內成功率有波動(如在95~100%之間),可以自動降級或人工降級,併發送告警;

(3)錯誤:比如可用率低於90%,或者數據庫連接池被打爆了,或者訪問量突然猛增到系統能承受的最大閥值,此時可以根據情況自動降級或者人工降級;

(4)嚴重錯誤:比如因爲特殊原因數據錯誤了,此時需要緊急人工降級。

緩存熱點key
使用緩存 + 過期時間的策略既可以加速數據讀寫,又保證數據的定期更新,這種模式基本能夠滿足絕大部分需求。但是有兩個問題如果同時出現,可能就會對應用造成致命的危害:

當前 key 是一個熱點 key( 可能對應應用的熱賣商品、熱點新聞、熱點評論等),併發量非常大。

重建緩存不能在短時間完成,可能是一個複雜計算,例如複雜的 SQL、多次 IO、多個依賴等。

在緩存失效的瞬間,有大量線程來重建緩存 ,造成後端負載加大,甚至可能會讓應用崩潰。

熱點 key 失效後大量線程重建緩存

要解決這個問題也不是很複雜,但是不能爲了解決這個問題給系統帶來更多的麻煩,所以需要制定如下目標:

減少重建緩存的次數

數據儘可能一致

較少的潛在危險

1)互斥鎖 (mutex key)

此方法只允許一個線程重建緩存,其他線程等待重建緩存的線程執行完,重新從緩存獲取數據即可

2)永遠不過期

永遠不過期”包含兩層意思:

從緩存層面來看,確實沒有設置過期時間,所以不會出現熱點 key 過期後產生的問題,也就是“物理”不過期。

從功能層面來看,爲每個 value 設置一個邏輯過期時間,當發現超過邏輯過期時間後,會使用單獨的線程去構建緩存

從實戰看,此方法有效杜絕了熱點 key 產生的問題,但唯一不足的就是重構緩存期間,會出現數據不一致的情況,這取決於應用方是否容忍這種不一致

作爲一個併發量較大的應用,在使用緩存時有三個目標
第一,加快用戶訪問速度,提高用戶體驗。
第二,降低後端負載,減少潛在的風險,保證系統平穩。
第三,保證數據“儘可能”及時更新。下面將按照這三個維度對上述兩種解決方案進行分析。

互斥鎖 (mutex key):這種方案思路比較簡單,但是存在一定的隱患,如果構建緩存過程出現問題或者時間較長,可能會存在死鎖和線程池阻塞的風險,但是這種方法能夠較好的降低後端存儲負載並在一致性上做的比較好。

” 永遠不過期 “:這種方案由於沒有設置真正的過期時間,實際上已經不存在熱點 key 產生的一系列危害,但是會存在數據不一致的情況,同時代碼複雜度會增大。

新手一枚,歡迎各位道友一起討論
如有錯誤,請在評論區指正

點關注,不迷路喲
老鐵,點個讚唄
收藏一手,隨時觀看

Redis官方文檔

更多優秀文章:
https://blog.csdn.net/qq_34337272/article/details/80012284
https://blog.csdn.net/yangzhong0808/article/details/81196472
https://blog.csdn.net/qq_36071795/article/details/83988177
https://blog.csdn.net/weixin_45154607/article/details/92969779

redis面試題庫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章