1. 程式人生 > >面試被吊打系列 - Redis原理

面試被吊打系列 - Redis原理

> 小張興沖沖去面試,結果被面試官吊打! 小張: 面試官,你好。我是來參加面試的。 面試官: 你好,小張。我看了你的簡歷,熟練掌握Redis,那麼我就隨便問你幾個Redis相關的問題吧。首先我的問題是,**Redis是單執行緒還是多執行緒呢?** 小張: Redis不同版本之間採用的執行緒模型是不一樣的,在Redis4.0版本之前使用的是單執行緒模型,在4.0版本之後增加了多執行緒的支援。 在4.0之前雖然我們說Redis是單執行緒,也只是說它的網路I/O執行緒以及Set 和 Get操作是由一個執行緒完成的。但是Redis的持久化、叢集同步還是使用其他執行緒來完成。 4.0之後添加了多執行緒的支援,主要是體現在大資料的非同步刪除功能上,例如 `unlink key`、`flushdb async`、`flushall async` 等 面試官: 回答的很好,**那為什麼Redis在4.0之前會選擇使用單執行緒?而且使用單執行緒還那麼快?** 小張: 選擇單執行緒個人覺得主要是使用簡單,不存在鎖競爭,可以在無鎖的情況下完成所有操作,不存在死鎖和執行緒切換帶來的效能和時間上的開銷,但同時單執行緒也不能完全發揮出多核CPU的效能。 至於為什麼單執行緒那麼快我覺得主要有以下幾個原因: 1. Redis 的大部分操作都在記憶體中完成,記憶體中的執行效率本身就很快,並且採用了高效的資料結構,比如雜湊表和跳錶。 2. 使用單執行緒避免了多執行緒的競爭,省去了多執行緒切換帶來的時間和效能開銷,並且不會出現死鎖。 3. 採用 I/O 多路複用機制處理大量客戶端的Socket請求,因為這是基於非阻塞的 I/O 模型,這就讓Redis可以高效地進行網路通訊,I/O的讀寫流程也不再阻塞。 面試官: 不錯,**那Redis是如何實現資料不丟失的呢?** 小張: Redis資料是儲存在記憶體中的,為了保證Redis資料不丟失,那就要把資料從記憶體儲存到磁碟上,以便在伺服器重啟後還能夠從磁碟中恢復原有資料,這就是Redis的資料持久化。Redis資料持久化有三種方式。 * AOF 日誌(Append Only File,檔案追加方式):記錄所有的操作命令,並以文字的形式追加到檔案中。 * RDB 快照(Redis DataBase):將某一個時刻的記憶體資料,以二進位制的方式寫入磁碟。 * 混合持久化方式:Redis 4.0 新增了混合持久化的方式,集成了 RDB 和 AOF 的優點。 面試官: 那你分別說說 **AOF和 RDB的實現原理吧。** 小張: AOF採用的是寫後日志的方式,Redis先執行命令把資料寫入記憶體,然後再記錄日誌到檔案中。AOF日誌記錄的是操作命令,不是實際的資料,如果採用AOF方法做故障恢復時需要將全量日誌都執行一遍。![image.png](https://img2020.cnblogs.com/blog/990993/202103/990993-20210308140048172-900229916.png) RDB採用的是記憶體快照的方式,它記錄的是某一時刻的資料,而不是操作,所以採用RDB方法做故障恢復時只需要直接把RDB檔案讀入記憶體即可,實現快速恢復。 面試官: 你剛提到了AOF採用的是 “寫後日志” 的方式,我們平時用的MySQL則採用的是 “寫前日誌”,那 **Redis為什麼要先執行命令,再把資料寫入日誌呢?** 小張:額頭開始冒汗,問的是些啥問題呀。。。 額,這個主要是由於Redis在寫入日誌之前,不對命令進行語法檢查,所以只記錄執行成功的命令,避免出現記錄錯誤命令的情況,而且在命令執行後再寫日誌不會阻塞當前的寫操作。 面試官: 那 **後寫日誌又有什麼風險呢?** 小張: 我... 這個我不會。 面試官: 好吧,後寫日誌主要有兩個風險可能會發生: * 資料可能會丟失: 如果 Redis 剛執行完命令,此時發生故障宕機,會導致這條命令存在丟失的風險。 * 可能阻塞其他操作: AOF 日誌其實也是在主執行緒中執行,所以當 Redis 把日誌檔案寫入磁碟的時候,還是會阻塞後續的操作無法執行。 我還有個問題是 **RDB做快照時會阻塞執行緒嗎?** 小張: Redis 提供了兩個命令來生成 RDB 快照檔案,分別是 `save` 和 `bgsave`。`save` 命令在主執行緒中執行,會導致阻塞。而 `bgsave` 命令則會建立一個子程序,用於寫入 RDB 檔案的操作,避免了對主執行緒的阻塞,這也是 Redis RDB 的預設配置。 面試官: **RDB 做快照的時候資料能修改嗎?** 小張: save是同步的會阻塞客戶端命令,bgsave的時候是可以修改的。 面試官: **那Redis是怎麼解決在bgsave做快照的時候允許資料修改呢?** 小張:(你咋還問。。。我™不會啊!) 額,這個我不太清楚... ![image.png](https://img2020.cnblogs.com/blog/990993/202103/990993-20210308140048598-812748747.png) 面試官: 這裡主要是利用 `bgsave`的子執行緒實現的,具體操作如下: * 如果主執行緒執行讀操作,則主執行緒和`bgsave` 子程序互相不影響; * 如果主執行緒執行寫操作,則被修改的資料會複製一份副本,然後`bgsave`子程序會把該副本資料寫入 RDB 檔案,在這個過程中,主執行緒仍然可以直接修改原來的資料。 ![image.png](https://img2020.cnblogs.com/blog/990993/202103/990993-20210308140048890-1235192062.png) 要注意,Redis 對 RDB 的執行頻率非常重要,因為這會影響快照資料的完整性以及 Redis 的穩定性,所以在 Redis 4.0 後,**增加了 AOF 和 RDB 混合的資料持久化機制:** 把資料以 RDB 的方式寫入檔案,再將後續的操作命令以 AOF 的格式存入檔案,既保證了 Redis 重啟速度,又降低資料丟失風險。 小張: 學到了學到了。 面試官: **那你再跟我說說Redis如何實現高可用吧?** 小張: Redis實現高可用主要有三種方式:主從複製、哨兵模式,以及 Redis 叢集。 **主從複製** 將從前的一臺 Redis 伺服器,同步資料到多臺從 Redis 伺服器上,即一主多從的模式,這個跟MySQL主從複製的原理一樣。 ![image.png](https://img2020.cnblogs.com/blog/990993/202103/990993-20210308140049140-1084035971.png) **哨兵模式** 使用 Redis 主從服務的時候,會有一個問題,就是當 Redis 的主從伺服器出現故障宕機時,需要手動進行恢復,為了解決這個問題,Redis 增加了哨兵模式(因為哨兵模式做到了可以監控主從伺服器,並且提供自動容災恢復的功能)。 ![image.png](https://img2020.cnblogs.com/blog/990993/202103/990993-20210308140049334-482422398.png) **Redis Cluster(叢集)** Redis Cluster 是一種分散式去中心化的執行模式,是在 Redis 3.0 版本中推出的 Redis 叢集方案,它將資料分佈在不同的伺服器上,以此來降低系統對單主節點的依賴,從而提高 Redis 服務的讀寫效能。 ![image.png](https://img2020.cnblogs.com/blog/990993/202103/990993-20210308140049643-656286929.png) 面試官: 使用哨兵模式在資料上有副本資料做保證,在可用性上又有哨兵監控,一旦master宕機會選舉salve節點為master節點,這種已經滿足了我們的生產環境需要,**那為什麼還需要使用叢集模式呢?** 小張: 額,哨兵模式歸根節點還是主從模式,在主從模式下我們可以通過增加salve節點來擴充套件讀併發能力,但是沒辦法擴充套件寫能力和儲存能力,儲存能力只能是master節點能夠承載的上限。所以為了擴充套件寫能力和儲存能力,我們就需要引入叢集模式。 面試官: **叢集中那麼多Master節點,redis cluster在儲存的時候如何確定選擇哪個節點呢?** 小張: 這應該是使用了某種hash演算法,但是我不太清楚。。。 ![image.png](https://img2020.cnblogs.com/blog/990993/202103/990993-20210308140049988-1168180351.png) 面試官: 那好,今天的面試就到這裡吧,你先回去等我們的面試通知。 小張: 好的,謝謝面試官,你能告訴我redis cluster怎麼實現節點選擇的嗎? 面試官: Redis Cluster採用的是**類一致性雜湊演算法實現節點選擇的**,至於什麼是一致性雜湊演算法你自己回去看看。 Redis Cluster將自己分成了16384個**Slot**(槽位),雜湊槽類似於資料分割槽,每個鍵值對都會根據它的 key,被對映到一個雜湊槽中,具體執行過程分為兩大步。 * 根據鍵值對的 key,按照 CRC16 演算法計算一個 16 bit 的值。 * 再用 16bit 值對 16384 取模,得到`0~16383` 範圍內的模數,每個模數代表一個相應編號的雜湊槽。 每個Redis節點負責處理一部分槽位,加入你有三個master節點 ABC,每個節點負責的槽位如下: | 節點 | 處理槽位 | | :-: | :-: | | A | 0-5000 | | B | 5001 - 10000 | | C | 10001 - 16383 | 這樣就實現了cluster節點的選擇。 好了,各位看官朋友們,Redis原理的這些面試點你們記住了嗎?希望你們的面試不會被這個問題難倒喲~ ![](https://img2020.cnblogs.com/blog/990993/202103/990993-20210308140134933-1325499953.png)