【乾貨】Redis面試題集錦

1. 你在實際項目中使用緩存有遇到什麼問題或者會遇到什麼問題你知道嗎?

  緩存和數據庫數據一致性問題:分佈式環境下非常容易出現緩存和數據庫間數據一致性問題,針對這一點,如果項目對緩存的要求是強一致性的,那麼就不要使用緩存。我們只能採取合適的策略來降低緩存和數據庫間數據不一致的概率,而無法保證兩者間的強一致性。合適的策略包括合適的緩存更新策略,更新數據庫後及時更新緩存、緩存失敗時增加重試機制

2.Redis雪崩瞭解嗎?

  我瞭解,目前電商首頁以及熱點數據都會去做緩存,一般緩存都是定時任務去刷新,或者查不到之後去更新緩存的,定時任務刷新就有一個問題。
  舉個栗子:如果首頁所有Key的失效時間都是12小時,中午12點刷新的,我零點有個大促活動大量用戶涌入,假設每秒6000個請求,本來緩存可以抗住每秒5000個請求,但是緩存中所有Key都失效了。此時6000個/秒的請求全部落在了數據庫上,數據庫必然扛不住,真實情況可能DBA都沒反應過來直接掛了,此時,如果沒什麼特別的方案來處理,DBA很着急,重啓數據庫,但是數據庫立馬又被新流量給打死了。這就是我理解的緩存雪崩。

3.如何應對Redis雪崩?

  處理緩存雪崩簡單,在批量往Redis存數據的時候,把每個Key的失效時間都加個隨機值就好了,這樣可以保證數據不會再同一時間大面積失效。

setRedis(key, value, time+Math.random()*10000;

  如果Redis是集羣部署,將熱點數據均勻分佈在不同的Redis庫中也能避免全部失效。
  或者設置熱點數據永不過期,有更新操作就更新緩存就好了(比如運維更新了首頁商品,那你刷下緩存就好了,不要設置過期時間),電商首頁的數據也可以用這個操作,保險。

4.瞭解緩存穿透和擊穿麼,可以說說他們跟雪崩的區別嗎?

  先說下緩存穿透吧,緩存穿透是指緩存和數據庫中都沒有的數據,而用戶(黑客)不斷髮起請求,舉個栗子:我們數據庫的id都是從1自增的,如果發起id=-1的數據或者id特別大不存在的數據,這樣的不斷攻擊導致數據庫壓力很大,嚴重會擊垮數據庫。

  至於緩存擊穿,這個跟緩存雪崩有點像,但是又有一點不一樣,緩存雪崩是因爲大面積的緩存失效,打崩了DB,而緩存擊穿不同的是緩存擊穿是指一個Key非常熱點,在不停地扛着大量的請求,大併發集中對這一個點進行訪問,當這個Key在失效的瞬間,持續的大併發直接落到了數據庫上,就在這個Key的點上擊穿了緩存。

5.如何解決緩存穿透和擊穿?

  對於緩存穿透,我會在接口層增加校驗,比如用戶鑑權,參數做校驗,不合法的校驗直接return,比如id做基礎校驗,id<=0直接攔截。
  採用布隆過濾器(Bloom Filter) 這個也能很好的預防緩存穿透的發生,他的原理就是利用高效的數據結構和算法快速判斷出你這個Key是否在數據庫中存在,不存在你return就好了,存在你就去查DB刷新KV再return。
   對於緩存擊穿, 設置熱點數據永不過期,或者 加上互斥鎖 就搞定了。

public static 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;
    }

6.redis這麼快,爲何是單線程的?

  Redis確實是單進程單線程的模型,因爲Redis完全是基於內存的操作,CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機器內存的大小或者網絡帶寬。既然單線程容易實現,而且CPU不會成爲瓶頸,那就順理成章的採用單線程的方案了,畢竟採用多線程會有很多麻煩。

7.Redis是單線程的,爲什麼還能這麼快?

  1.Redis完全基於內存,絕大部分請求是純粹的內存操作,非常迅速,數據存在內存中,類似於HashMap,HashMap的優勢就是查找和操作的時間複雜度是O(1)。
  2.數據結構簡單,對數據操作也簡單。
  3.採用單線程,避免了不必要的上下文切換和競爭條件,不存在多線程導致的CPU切換,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有死鎖問題導致的性能消耗。
  4.使用多路複用IO模型,非阻塞IO

8.Redis和Memcached的區別是什麼?

  1、存儲方式上:memcache會把數據全部存在內存之中,斷電後會掛掉,數據不能超過內存大小。redis有部分數據存在硬盤上,這樣能保證數據的持久性。
  2、數據支持類型上:memcache對數據類型的支持簡單,只支持簡單的key-value,而redis支持五種數據類型。
  3、**性能上:**由於 Redis 只使用單核,而 Memcached 可以使用多核,所以平均每一個核上 Redis 在存儲小數據時比 Memcached 性能更高;而在 100k 以上的數據中,Memcached 性能要高於 Redis。
  4、使用底層模型不同:它們之間底層實現方式以及與客戶端之間通信的應用協議不一樣。redis直接自己構建了VM機制,因爲一般的系統調用系統函數的話,會浪費一定的時間去移動和請求。
  5、value的大小:redis可以達到1GB,而memcache只有1MB。
  6、集羣模式:Redis 原生便能支持 Cluster 模式,而 Memcached 沒有原生的集羣模式,需要依靠客戶端來實現往集羣中分片寫入數據。

9.redis的淘汰策略有哪些?
  Redis有六種淘汰策略:
在這裏插入圖片描述
補充:Redis4.0加入了LFU(least frequency use)淘汰策略,包括volatile-lfu和allkeys-lfu,通過統計訪問頻率,將訪問頻率最少,即最不經常使用的KV淘汰。

10.你對redis的持久化機制瞭解嗎?

  redis爲了保證效率,數據緩存在了內存中,但是會週期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件中,以保證數據的持久化。Redis的持久化策略有兩種:

  • RDB: 快照形式是直接把內存中的數據保存到一個dump的文件中,定時保存,保存策略。
  • AOF: 把所有的對Redis的服務器進行修改的命令都存到一個文件裏,命令的集合。

  Redis默認是快照RDB的持久化方式。當Redis重啓的時候,它會優先使用AOF文件來還原數據集,因爲AOF文件保存的數據集通常比RDB文件所保存的數據集更完整。你甚至可以關閉持久化功能,讓數據只在服務器運行時存。

11.RDB是怎麼工作的?

  默認Redis是會以快照"RDB"的形式將數據持久化到磁盤的一個二進制文件dump.rdb。工作原理:當Redis需要做持久化時,Redis會fork一個子進程,子進程將數據寫到磁盤上一個臨時RDB文件中。當子進程完成寫臨時文件後,將原來的RDB替換掉,這樣的好處是可以copy-on-write

  RDB的優點這種文件非常適合用於備份。比如,你可以在最近的24小時內,每小時備份一次,並且在每個月的每一天也備份一個RDB文件。這樣的話,即使遇上問題,也可以隨時將數據集還原到不同的版本。RDB非常適合災難恢復。
  RDB的缺點:如果你需要儘量避免在服務器故障時丟失數據,那麼RDB不合適你。

12.AOF是怎麼工作的?
  使用AOF做持久化,每一個寫命令都通過write函數追加到appendonly.aof中,配置方式如下:

appendfsync yes   
appendfsync always     #每次有數據修改發生時都會寫入AOF文件。
appendfsync everysec   #每秒鐘同步一次,該策略爲AOF的缺省策略。

  AOF可以做到全程持久化,只需要在配置中開啓 appendonly yes。這樣redis每執行一個修改數據的命令,都會把它添加到AOF文件中,當redis重啓時,將會讀取AOF文件進行重放,恢復到redis關閉前的最後時刻。

  AOF的優點:會讓redis變得非常耐久。可以設置不同的fsync策略,aof的默認策略是每秒鐘fsync一次,在這種配置下,就算髮生故障停機,也最多丟失一秒鐘的數據。
  AOF的缺點:對於相同的數據集來說,AOF的文件體積通常要大於RDB文件的體積。根據所使用的fsync策略,AOF的速度可能會慢於RDB。

13.如何選用持久化策略?

  如果你非常關心你的數據,但仍然可以承受數分鐘內的數據丟失,那麼可以額只使用RDB持久。AOF將Redis執行的每一條命令追加到磁盤中,處理巨大的寫入會降低Redis的性能,不知道你是否可以接受。
  數據庫備份和災難恢復:定時生成RDB快照非常便於進行數據庫備份,並且RDB恢復數據集的速度也要比AOF恢復的速度快。
  當然了,redis支持同時開啓RDB和AOF,系統重啓後,redis會優先使用AOF來恢復數據,這樣丟失的數據會最少。

14.多個系統併發操作Redis帶來的數據問題是什麼,如何解決?
  系統A、B、C三個系統,分別去操作Redis的同一個Key,本來順序是1,2,3是正常的,但是因爲系統A網絡突然抖動了一下,B,C在他前面操作了Redis,這樣數據就出錯了。
在這裏插入圖片描述
  某個時刻多個系統實例都去更新某個 key,可以基於 Zookeeper 實現分佈式鎖。每個系統通過 Zookeeper 獲取分佈式鎖,確保同一時間,只能有一個系統實例在操作某個 Key,別人都不允許讀和寫。
  你要寫入緩存的數據,都是從 MySQL 裏查出來的,都得寫入 MySQL 中,寫入 MySQL 中的時候必須保存一個時間戳,從 MySQL 查出來的時候,時間戳也查出來。每次要寫之前,先判斷一下當前這個 Value 的時間戳是否比緩存裏的 Value 的時間戳要新。如果是的話,那麼可以寫,否則,就不能用舊的數據覆蓋新的數據。

15.只要用緩存,就可能會涉及到緩存與數據庫雙存儲雙寫,只要是雙寫,就一定會有數據一致性的問題,那麼你如何解決一致性問題?

  一般來說,如果允許緩存可以稍微的跟數據庫偶爾有不一致的情況,也就是說如果你的系統不是嚴格要求 “緩存+數據庫” 必須保持一致性的話,最好不要做這個方案,即:讀請求和寫請求串行化,串到一個內存隊列裏去。
  串行化可以保證一定不會出現不一致的情況,但是它也會導致系統的吞吐量大幅度降低,用比正常情況下多幾倍的機器去支撐線上的一個請求。把一些列的操作都放到隊列裏面,順序肯定不會亂,但是併發高了,這隊列很容易阻塞。

16.你瞭解最經典的KV+DB讀寫模式麼?

  最經典的緩存+數據庫讀寫的模式,就是 Cache Aside Pattern

  • 讀的時候,先讀緩存,緩存沒有的話,就讀數據庫,然後取出數據後放入緩存,同時返回響應。
  • 更新的時候,先更新數據庫,然後再刪除緩存

17.Redis 的線程模型瞭解麼?

  Redis 內部使用文件事件處理器 file event handler,這個文件事件處理器是單線程的,所以 Redis 才叫做單線程的模型。它採用 IO 多路複用機制同時監聽多個 Socket,根據 Socket 上的事件來選擇對應的事件處理器進行處理。

  文件事件處理器的結構包含 4 個部分:

  • 多個 Socket
  • IO 多路複用程序
  • 文件事件分派器
  • 事件處理器(連接應答處理器、命令請求處理器、命令回覆處理器)

  多個 Socket 可能會併發產生不同的操作,每個操作對應不同的文件事件,但是 IO 多路複用程序會監聽多個 Socket,會將 Socket 產生的事件放入隊列中排隊,事件分派器每次從隊列中取出一個事件,把該事件交給對應的事件處理器進行處理。

18.redis單節點存在單點故障問題,爲了解決單點問題,一般都需要對redis配置從節點,然後使用哨兵來監聽主節點的存活狀態,如果主節點掛掉,從節點能繼續提供緩存功能,你能說說redis主從複製的過程和原理嗎?

  主從配置結合哨兵模式能解決單點故障問題,提高redis可用性。從節點僅提供讀操作,主節點提供寫操作。對於讀多寫少的狀況,可給主節點配置多個從節點,從而提高響應效率。

  主從複製的過程是這樣的:

  • 從節點執行slaveof[masterIP][masterPort],保存主節點信息
  • 從節點中的定時任務發現主節點信息,建立和主節點的socket連接
  • 從節點發送Ping信號,主節點返回Pong,兩邊能互相通信
  • 連接建立後,主節點將所有數據發送給從節點(數據同步)
  • 主節點把當前的數據同步給從節點後,便完成了複製的建立過程。接下來,主節點就會持續的把寫命令發送給從節點,保證主從數據一致性。

19.你能詳細說下數據同步的過程嗎?

  redis2.8之前使用sync[runId][offset]同步命令,redis2.8之後使用psync[runId][offset]命令。兩者不同在於,sync命令僅支持全量複製過程,psync支持全量和部分複製。

  runId: 每個redis節點啓動都會生成唯一的uuid,每次redis重啓後,runId都會發生變化。
  offset: 主節點和從節點都各自維護自己的主從複製偏移量offset,當主節點有寫入命令時,offset=offset+命令的字節長度。從節點在收到主節點發送的命令後,也會增加自己的offset,並把自己的offset發送給主節點。這樣,主節點同時保存自己的offset和從節點的offset,通過對比offset來判斷主從節點數據是否一致。
  repl_backlog_size: 保存在主節點上的一個固定長度的先進先出隊列,默認大小是1MB。

  主節點發送數據給從節點過程中,主節點還會進行一些寫操作,這時候的數據存儲在複製緩衝區中。從節點同步主節點數據完成後,主節點將緩衝區的數據繼續發送給從節點,用於部分複製。
  主節點響應寫命令時,不但會把命名發送給從節點,還會寫入複製積壓緩衝區,用於複製命令丟失的數據補救。
在這裏插入圖片描述

  上面是psync的執行流程。從節點發送psync[runId][offset]命令,主節點有三種響應:

  • FULLRESYNC:第一次連接,進行全量複製
  • CONTINUE:進行部分複製
  • ERR:不支持psync命令,進行全量複製

20.你能具體說下全量複製和部分複製的過程嗎?
在這裏插入圖片描述

  上面是全量複製的流程。主要有以下幾步:

  • 從節點發送psync ? -1命令(因爲第一次發送,不知道主節點的runId,所以爲?,因爲是第一次複製,所以offset=-1)。
  • 主節點發現從節點是第一次複製,返回FULLRESYNC {runId} {offset},runId是主節點的runId,offset是主節點目前的offset。
  • 從節點接收主節點信息後,保存到info中。
  • 主節點在發送FULLRESYNC後,啓動bgsave命令,生成RDB文件(數據持久化)。
  • 主節點發送RDB文件給從節點。到從節點加載數據完成這段期間主節點的寫命令放入緩衝區。
  • 從節點清理自己的數據庫數據。
  • 從節點加載RDB文件,將數據保存到自己的數據庫中。
    如果從節點開啓了AOF,從節點會異步重寫AOF文件。

  關於部分複製有以下幾點說明:
  1、部分複製主要是Redis針對全量複製的過高開銷做出的一種優化措施,使用psync[runId][offset] 命令實現。當從節點正在複製主節點時,如果出現網絡閃斷或者命令丟失等異常情況時,從節點會向主節點要求補發丟失的命令數據,主節點的複製積壓緩衝區將這部分數據直接發送給從節點,這樣就可以保持主從節點複製的一致性。補發的這部分數據一般遠遠小於全量數據。
  2、主從連接中斷期間主節點依然響應命令,但因複製連接中斷命令無法發送給從節點,不過主節點內的複製積壓緩衝區依然可以保存最近一段時間的寫命令數據。
  3、當主從連接恢復後,由於從節點之前保存了自身已複製的偏移量和主節點的運行ID。因此會把它們當做psync參數發送給主節點,要求進行部分複製。
  4、主節點接收到psync命令後首先覈對參數runId是否與自身一致,如果一致,說明之前複製的是當前主節點;之後根據參數offset在複製積壓緩衝區中查找,如果offset之後的數據存在,則對從節點發送+COUTINUE命令,表示可以進行部分複製。因爲緩衝區大小固定,若發生緩衝溢出,則進行全量複製。
  5、主節點根據偏移量把複製積壓緩衝區裏的數據發送給從節點,保證主從複製進入正常狀態。

21.主從複製會存在哪些問題呢?

  主從複製會存在以下問題:

  • 一旦主節點宕機,從節點晉升爲主節點,同時需要修改應用方的主節點地址,還需要命令所有從節點去複製新的主節點,整個過程需要人工干預。
  • 主節點的寫能力受到單機的限制。
  • 主節點的存儲能力受到單機的限制。
  • 原生複製的弊端在早期的版本中也會比較突出,比如:redis複製中斷後,從節點會發起psync。此時如果同步不成功,則會進行全量同步,主庫執行全量備份的同時,可能會造成毫秒或秒級的卡頓。

  比較主流的解決方案是哨兵。

22.哨兵有哪些功能?

在這裏插入圖片描述
  如圖是Redis Sentinel(哨兵)的架構圖。Redis Sentinel(哨兵)主要功能包括主節點存活檢測、主從運行情況檢測、自動故障轉移、主從切換。Redis Sentinel最小配置是一主一從。

  Redis的Sentinel系統可以用來管理多個Redis服務器,該系統可以執行以下四個任務:

  • 監控:不斷檢查主服務器和從服務器是否正常運行。
  • 通知:當被監控的某個redis服務器出現問題,Sentinel通過API腳本向管理員或者其他應用程序發出通知。
  • 自動故障轉移:當主節點不能正常工作時,Sentinel會開始一次自動的故障轉移操作,它會將與失效主節點是主從關係的其中一個從節點升級爲新的主節點,並且將其他的從節點指向新的主節點,這樣人工干預就可以免了。
  • 配置提供者:在Redis Sentinel模式下,客戶端應用在初始化時連接的是Sentinel節點集合,從中獲取主節點的信息。
發佈了125 篇原創文章 · 獲贊 119 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章