媽媽再也不擔心我面試被Redis問得臉都綠了
- 2020 年 3 月 26 日
- 筆記
長文前排提醒,收藏向前排提醒,素質三連 (轉發 + 在看 + 留言) 前排提醒!
前言
Redis 作為一個開源的,高級的鍵值存儲和一個適用的解決方案,已經越來越在構建 「高性能」、「可擴展」 的 Web 應用上發揮着舉足輕重的作用。
當今互聯網技術架構中 Redis 已然成為了應用得最廣泛的中間件之一,它也是中高級後端工程 技術面試 中面試官最喜歡問的工程技能之一,不僅僅要求着我們對 基本的使用 進行掌握,更要深層次地理解 Redis 內部實現 的細節原理。
熟練掌握 Redis,甚至可以毫不誇張地說已經半隻腳踏入心儀的公司了。下面我們一起來盤點回顧一下 Redis 的面試經典問題,就不要再被面試官問得 臉都綠了 呀!
- Ps: 我把 重要的知識點 都做成了 圖片,希望各位 "用餐愉快"。(不錯記得付餐費.. 點個贊留個言..)
一、基礎篇
什麼是 Redis ?
先解釋 Redis 基本概念
Redis (Remote Dictionary Server) 是一個使用 C 語言 編寫的,開源的 (BSD許可) 高性能 非關係型 (NoSQL) 的 鍵值對數據庫。
簡單提一下 Redis 數據結構
Redis 可以存儲 鍵 和 不同類型數據結構值 之間的映射關係。鍵的類型只能是字符串,而值除了支持最 基礎的五種數據類型 外,還支持一些 高級數據類型:
一定要說出一些高級數據結構 *(當然你自己也要了解.. 下面會說到的別擔心)*,這樣面試官的眼睛才會亮。
Redis 小總結
與傳統數據庫不同的是 Redis 的數據是 存在內存 中的,所以 讀寫速度 非常 快,因此 Redis 被廣泛應用於 緩存 方向,每秒可以處理超過 10
萬次讀寫操作,是已知性能最快的 Key-Value 數據庫。另外,Redis 也經常用來做 分佈式鎖。
除此之外,Redis 支持事務 、持久化、LUA腳本、LRU驅動事件、多種集群方案。
Redis 優缺點
優點
- 讀寫性能優異, Redis能讀的速度是
110000
次/s,寫的速度是81000
次/s。 - 支持數據持久化,支持 AOF 和 RDB 兩種持久化方式。
- 支持事務,Redis 的所有操作都是原子性的,同時 Redis 還支持對幾個操作合併後的原子性執行。
- 數據結構豐富,除了支持 string 類型的 value 外還支持 hash、set、zset、list 等數據結構。
- 支持主從複製,主機會自動將數據同步到從機,可以進行讀寫分離。
缺點
- 數據庫 容量受到物理內存的限制,不能用作海量數據的高性能讀寫,因此 Redis 適合的場景主要局限在較小數據量的高性能操作和運算上。
- Redis 不具備自動容錯和恢復功能,主機從機的宕機都會導致前端部分讀寫請求失敗,需要等待機器重啟或者手動切換前端的 IP 才能恢復。
- 主機宕機,宕機前有部分數據未能及時同步到從機,切換 IP 後還會引入數據不一致的問題,降低了 系統的可用性。
- Redis 較難支持在線擴容,在集群容量達到上限時在線擴容會變得很複雜。為避免這一問題,運維人員在系統上線時必須確保有足夠的空間,這對資源造成了很大的浪費。
為什麼要用緩存?為什麼使用 Redis?
提一下現在 Web 應用的現狀
在日常的 Web 應用對數據庫的訪問中,讀操作的次數遠超寫操作,比例大概在 1:9 到 3:7,所以需要讀的可能性是比寫的可能大得多的。當我們使用 SQL 語句去數據庫進行讀寫操作時,數據庫就會 去磁盤把對應的數據索引取回來,這是一個相對較慢的過程。
使用 Redis or 使用緩存帶來的優勢
如果我們把數據放在 Redis 中,也就是直接放在內存之中,讓服務端直接去讀取內存中的數據,那麼這樣 速度 明顯就會快上不少 *(高性能)*,並且會 極大減小數據庫的壓力 (特別是在高並發情況下)。
記得是 兩個角度 啊.. 高性能 和 高並發..
也要提一下使用緩存的考慮
但是使用內存進行數據存儲開銷也是比較大的,限於成本 的原因,一般我們只是使用 Redis 存儲一些 常用和主要的數據,比如用戶登錄的信息等。
一般而言在使用 Redis 進行存儲的時候,我們需要從以下幾個方面來考慮:
- 業務數據常用嗎?命中率如何? 如果命中率很低,就沒有必要寫入緩存;
- 該業務數據是讀操作多,還是寫操作多? 如果寫操作多,頻繁需要寫入數據庫,也沒有必要使用緩存;
- 業務數據大小如何? 如果要存儲幾百兆位元組的文件,會給緩存帶來很大的壓力,這樣也沒有必要;
在考慮了這些問題之後,如果覺得有必要使用緩存,那麼就使用它!
使用緩存會出現什麼問題?
一般來說有如下幾個問題,回答思路遵照 是什麼 → 為什麼 → 怎麼解決:
- 緩存雪崩問題;
- 緩存穿透問題;
- 緩存和數據庫雙寫一致性問題;
緩存雪崩問題
另外對於 "Redis 掛掉了,請求全部走數據庫" 這樣的情況,我們還可以有如下的思路:
- 事發前:實現 Redis 的高可用(主從架構 + Sentinel 或者 Redis Cluster),盡量避免 Redis 掛掉這種情況發生。
- 事發中:萬一 Redis 真的掛了,我們可以設置本地緩存(ehcache) + 限流(hystrix),盡量避免我們的數據庫被幹掉(起碼能保證我們的服務還是能正常工作的)
- 事發後:Redis 持久化,重啟後自動從磁盤上加載數據,快速恢復緩存數據。
緩存穿透問題
緩存與數據庫雙寫一致問題
雙寫一致性上圖還是稍微粗糙了些,你還需要知道兩種方案 (先操作數據庫和先操作緩存) 分別都有什麼優勢和對應的問題,這裡不作贅述,可以參考一下下方的文章,寫得非常詳細。
- 面試前必須要知道的Redis面試題 | Java3y – https://mp.weixin.qq.com/s/3Fmv7h5p2QDtLxc9n1dp5A
Redis 為什麼早期版本選擇單線程?
官方解釋
因為 Redis 是基於內存的操作,CPU 不是 Redis 的瓶頸,Redis 的瓶頸最有可能是 機器內存的大小 或者 網絡帶寬。既然單線程容易實現,而且 CPU 不會成為瓶頸,那就順理成章地採用單線程的方案了。
簡單總結一下
- 使用單線程模型能帶來更好的 可維護性,方便開發和調試;
- 使用單線程模型也能 並發 的處理客戶端的請求;*(I/O 多路復用機制)*
- Redis 服務中運行的絕大多數操作的 性能瓶頸都不是 CPU;
強烈推薦 各位親看一下這篇文章:
- 為什麼 Redis 選擇單線程模型 · Why’s THE Design? – https://draveness.me/whys-the-design-redis-single-thread
Redis 為什麼這麼快?
簡單總結:
- 純內存操作:讀取不需要進行磁盤 I/O,所以比傳統數據庫要快上不少;*(但不要有誤區說磁盤就一定慢,例如 Kafka 就是使用磁盤順序讀取但仍然較快)*
- 單線程,無鎖競爭:這保證了沒有線程的上下文切換,不會因為多線程的一些操作而降低性能;
- 多路 I/O 復用模型,非阻塞 I/O:採用多路 I/O 復用技術可以讓單個線程高效的處理多個網絡連接請求(盡量減少網絡 IO 的時間消耗);
- 高效的數據結構,加上底層做了大量優化:Redis 對於底層的數據結構和內存佔用做了大量的優化,例如不同長度的字符串使用不同的結構體表示,HyperLogLog 的密集型存儲結構等等..
二、數據結構篇
簡述一下 Redis 常用數據結構及實現?
首先在 Redis 內部會使用一個 RedisObject 對象來表示所有的 key
和 value
:
其次 Redis 為了 平衡空間和時間效率,針對 value
的具體類型在底層會採用不同的數據結構來實現,下圖展示了他們之間的映射關係:*(好像亂糟糟的,但至少能看清楚..)*
Redis 的 SDS 和 C 中字符串相比有什麼優勢?
先簡單總結一下
C 語言使用了一個長度為 N+1
的字符數組來表示長度為 N
的字符串,並且字符數組最後一個元素總是