博客 / 詳情

返回

騰訊一面,感覺問Redis的難度不是很大

前不久,有位朋友去騰訊面試,他説被問到了很多關於 Redis 的問題,比如為什麼用 Redis 作為 MySQL 的緩存?Redis 中大量 key 集中過期怎麼辦?如何保證緩存和數據庫數據的一致性?我將它們整理出來,跟大家一起來探討如何回答這些問題,希望對大家有所幫助。

Redis 為什麼這麼快?

為什麼用 Redis 作為 MySQL 的緩存?

Redis 除了做緩存,還能做什麼?

使用 redis 分佈式鎖,如何合理設置過期時間?

Redis 單線程模型瞭解嗎?

Redis 中大量 key 集中過期怎麼辦?

如何保證緩存和數據庫數據的一致性?

Redis 為什麼這麼快?

Redis 內部做了非常多的性能優化,比較重要的有下面幾點:

  1. Redis 基於內存,內存的訪問速度比磁盤快很多;
  2. Redis 基於 Reactor 模式設計開發了一套高效的事件處理模型,主要是單線程事件循環IO 多路複用
  3. Redis 內置了多種優化過後的數據類型/結構實現,性能非常高。
  4. Redis 通信協議實現簡單且解析高效。

擴展:那既然都這麼快了,為什麼不直接用 Redis 當主數據庫呢?

  • 主要是因為內存成本太高且 Redis 提供的數據持久化仍然有數據丟失的風險。

為什麼用 Redis 作為 MySQL 的緩存?

主要是因為 Redis 具備高性能高併發兩種特性。下面來詳細介紹一個高性能和高併發。(這個問題是個開放題,我的答案僅供參考)

高性能

假如用户第一次訪問 MySQL 中的某些數據。這個過程會比較慢,因為是從硬盤上讀取的。將該用户訪問的數據緩存在 Redis 中,這樣下一次再訪問這些數據的時候就可以直接從緩存中獲取了,操作 Redis 緩存就是直接操作內存,所以速度相當快。

如果 MySQL 中的對應數據改變的之後,同步改變 Redis 緩存中相應的數據即可。

高併發

單台設備的 Redis 的 QPS(Query Per Second,每秒鐘處理完請求的次數) 是 MySQL 的 10 倍,Redis 單機的 QPS 能輕鬆破 10w,而 MySQL 單機的 QPS 很難破 1w。

所以,直接訪問 Redis 能夠承受的請求是遠遠大於直接訪問 MySQL 的,所以我們可以考慮把數據庫中的部分數據轉移到緩存中去,這樣用户的一部分請求會直接到緩存這裏而不用經過數據庫。

Redis 除了做緩存,還能做什麼?

  1. 分佈式鎖:通過 Redis 來做分佈式鎖是一種比較常見的方式。通常情況下,我們都是基於 Redisson 來實現分佈式鎖。
  2. 限流:一般是通過 Redis + Lua 腳本的方式來實現限流。如果不想自己寫 Lua 腳本的話,也可以直接利用 Redisson 中的 RRateLimiter 來實現分佈式限流,其底層實現就是基於 Lua 代碼+令牌桶算法。
  3. 消息隊列:Redis 自帶的 List 數據結構可以作為一個簡單的隊列使用。Redis 5.0 中增加的 Stream 類型的數據結構更加適合用來做消息隊列。它比較類似於 Kafka,有主題和消費組的概念,支持消息持久化以及 ACK 機制。
  4. 延時隊列:Redisson 內置了延時隊列(基於 Sorted Set 實現的)。
  5. 分佈式 Session:利用 String 或者 Hash 數據類型保存 Session 數據,所有的服務器都可以訪問。
  6. 複雜業務場景:通過 Redis 以及 Redis 擴展(比如 Redisson)提供的數據結構,我們可以很方便地完成很多複雜的業務場景比如通過 Bitmap 統計活躍用户、通過 Sorted Set 維護排行榜。

使用 redis 分佈式鎖,如何合理設置過期時間?

Redis 單線程模型瞭解嗎?

Redis 基於 Reactor 模式設計開發了一套高效的事件處理模型 ,這套事件處理模型對應的是 Redis 中的文件事件處理器(file event handler)。

由於文件事件處理器是單線程方式運行的,所以我們一般都説 Redis 是單線程模型。

<u>面試官又問了一個小問題,感覺回答的不錯</u>

既然是單線程,那怎麼監聽大量的客户端連接呢?

Redis 通過 IO 多路複用程序 來監聽來自客户端的大量連接(或者説是監聽多個 socket),它會將感興趣的事件及類型(讀、寫)註冊到內核中並監聽每個事件是否發生。

I/O 多路複用技術的使用讓 Redis 不需要額外創建多餘的線程來監聽客户端的大量連接,降低了資源的消耗,這樣使用的好處是非常明顯的。

使用 redis 分佈式鎖,如何合理設置過期時間?

需要考慮如下幾個因素:

  1. 任務執行時間:確保過期時間大於預期的最長執行時間,以免任務還在執行過程中鎖就被自動釋放,導致併發問題
  2. 鎖自動續期:如果使用了支持鎖自動續期的 Redis 客户端庫(如 Redisson),在持有鎖的線程還在執行任務期間,可以定期自動延長鎖的有效期,這樣可以減小因鎖過期導致的併發問題。
  3. 鎖競爭激烈程度:如果鎖的競爭非常激烈,過期時間不宜設置得太短,否則可能會頻繁觸發鎖的競爭,消耗更多資源。反之,如果鎖的競爭不大,可以適當縮短過期時間,更快地回收鎖資源。
  4. 死鎖檢測與處理:設定一個合理的最大等待時間,超過這個時間還沒有釋放的鎖可以被認為是持有鎖的客户端出現問題,可以通過監控和相應的邏輯來處理此類死鎖。
  5. 網絡延遲和異常恢復:考慮到網絡不穩定等因素,過期時間還應該預留一部分用於處理網絡延遲或客户端異常恢復的情況。過期時間太短可能導致客户端未能及時釋放鎖或重新獲取鎖。
  6. 鎖釋放的可靠性:使用 lua 腳本來保證解鎖操作的原子性,同時結合 watch 命令或事務處理,以最大程度地確保鎖在業務邏輯完成後能夠正確釋放,降低對過期時間依賴的程度。

Redis 中大量 key 集中過期怎麼辦?

首先回答 大量 key 集中過期可能出現的問題:

  • 請求延遲增加: Redis 在處理過期 key 時需要消耗 CPU 資源,如果過期 key 數量龐大,會導致 Redis 實例的 CPU 佔用率升高,進而影響其他請求的處理速度,造成延遲增加。
  • 內存佔用過高: 過期的 key 雖然已經失效,但在 Redis 真正刪除它們之前,仍然會佔用內存空間。如果過期 key 沒有及時清理,可能會導致內存佔用過高,甚至引發內存溢出。

之後再回答 可以採取的方案:

  • 1.儘量避免 key 集中過期: 在設置鍵的過期時間時儘量隨機一點。
  • 2.開啓 lazy free 機制: 修改 redis.conf 配置文件,將 lazyfree-lazy-expire 參數設置為 yes,即可開啓 lazy free 機制。開啓 lazy free 機制後,Redis 會在後台異步刪除過期的 key,不會阻塞主線程的運行,從而降低對 Redis 性能的影響。

如何保證緩存和數據庫數據的一致性?

其實感覺聊聊 Cache Aside 這個策略就可以了,細説的話沒啥太大必要。

下面來説説 Cache Aside 策略:

Cache Aside 中遇到寫請求是這樣的,更新數據庫,然後直接刪除緩存。

但是必須是這兩步都成功,才能解決緩存和數據庫數據不一致的問題。

關於更新數據庫成功,而刪除緩存這一步失敗的這種情況,是可能發生的,簡單説有兩個解決方案:

  • 緩存失效時間變短(不推薦,治標不治本):我們讓緩存數據的過期時間變短,這樣的話緩存就會從數據庫中加載數據。用户會反饋在一段時間後,才能更新數據哦!!!
  • 增加緩存更新重試機制(常用):如果緩存服務當前不可用導致緩存刪除失敗的話,我們就隔一段時間進行重試,重試次數可以自己定。不過,這裏更適合引入消息隊列實現異步重試,將刪除緩存重試的消息投遞到消息隊列,然後由專門的消費者來重試,直到成功。

恭喜你,面試通過!!!

就業陪跑訓練營學員投稿

歡迎關注 ❤

我們搞了一個免費的面試真題共享羣,互通有無,一起刷題進步。

沒準能讓你能刷到自己意向公司的最新面試題呢。

感興趣的朋友們可以加我微信:wangzhongyang1993,備註:面試羣。

user avatar u_16213429 頭像 mokeywie 頭像
2 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.