面試被吊打系列 - Redis原理

JAVA日知錄發表於2021-03-08

小張興沖沖去面試,結果被面試官吊打!

小張:

面試官,你好。我是來參加面試的。

面試官:

你好,小張。我看了你的簡歷,熟練掌握Redis,那麼我就隨便問你幾個Redis相關的問題吧。首先我的問題是,Redis是單執行緒還是多執行緒呢?

小張:

Redis不同版本之間採用的執行緒模型是不一樣的,在Redis4.0版本之前使用的是單執行緒模型,在4.0版本之後增加了多執行緒的支援。

在4.0之前雖然我們說Redis是單執行緒,也只是說它的網路I/O執行緒以及Set 和 Get操作是由一個執行緒完成的。但是Redis的持久化、叢集同步還是使用其他執行緒來完成。

4.0之後新增了多執行緒的支援,主要是體現在大資料的非同步刪除功能上,例如 unlink keyflushdb asyncflushall 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

RDB採用的是記憶體快照的方式,它記錄的是某一時刻的資料,而不是操作,所以採用RDB方法做故障恢復時只需要直接把RDB檔案讀入記憶體即可,實現快速恢復。

面試官:

你剛提到了AOF採用的是 “寫後日志” 的方式,我們平時用的MySQL則採用的是 “寫前日誌”,那 Redis為什麼要先執行命令,再把資料寫入日誌呢?

小張:額頭開始冒汗,問的是些啥問題呀。。。

額,這個主要是由於Redis在寫入日誌之前,不對命令進行語法檢查,所以只記錄執行成功的命令,避免出現記錄錯誤命令的情況,而且在命令執行後再寫日誌不會阻塞當前的寫操作。

面試官:

後寫日誌又有什麼風險呢?

小張:

我... 這個我不會。

面試官:

好吧,後寫日誌主要有兩個風險可能會發生:

  • 資料可能會丟失: 如果 Redis 剛執行完命令,此時發生故障當機,會導致這條命令存在丟失的風險。
  • 可能阻塞其他操作: AOF 日誌其實也是在主執行緒中執行,所以當 Redis 把日誌檔案寫入磁碟的時候,還是會阻塞後續的操作無法執行。

我還有個問題是 RDB做快照時會阻塞執行緒嗎?

小張:

Redis 提供了兩個命令來生成 RDB 快照檔案,分別是 savebgsavesave 命令在主執行緒中執行,會導致阻塞。而 bgsave 命令則會建立一個子程式,用於寫入 RDB 檔案的操作,避免了對主執行緒的阻塞,這也是 Redis RDB 的預設配置。

面試官:

RDB 做快照的時候資料能修改嗎?

小張:

save是同步的會阻塞客戶端命令,bgsave的時候是可以修改的。

面試官:

那Redis是怎麼解決在bgsave做快照的時候允許資料修改呢?

小張:(你咋還問。。。我™不會啊!)

額,這個我不太清楚...

image.png

面試官:

這裡主要是利用 bgsave的子執行緒實現的,具體操作如下:

  • 如果主執行緒執行讀操作,則主執行緒和bgsave 子程式互相不影響;
  • 如果主執行緒執行寫操作,則被修改的資料會複製一份副本,然後bgsave子程式會把該副本資料寫入 RDB 檔案,在這個過程中,主執行緒仍然可以直接修改原來的資料。

image.png

要注意,Redis 對 RDB 的執行頻率非常重要,因為這會影響快照資料的完整性以及 Redis 的穩定性,所以在 Redis 4.0 後,增加了 AOF 和 RDB 混合的資料持久化機制: 把資料以 RDB 的方式寫入檔案,再將後續的操作命令以 AOF 的格式存入檔案,既保證了 Redis 重啟速度,又降低資料丟失風險。

小張:

學到了學到了。

面試官:

那你再跟我說說Redis如何實現高可用吧?

小張:

Redis實現高可用主要有三種方式:主從複製、哨兵模式,以及 Redis 叢集。

主從複製

將從前的一臺 Redis 伺服器,同步資料到多臺從 Redis 伺服器上,即一主多從的模式,這個跟MySQL主從複製的原理一樣。

image.png

哨兵模式

使用 Redis 主從服務的時候,會有一個問題,就是當 Redis 的主從伺服器出現故障當機時,需要手動進行恢復,為了解決這個問題,Redis 增加了哨兵模式(因為哨兵模式做到了可以監控主從伺服器,並且提供自動容災恢復的功能)。

image.png

Redis Cluster(叢集)

Redis Cluster 是一種分散式去中心化的執行模式,是在 Redis 3.0 版本中推出的 Redis 叢集方案,它將資料分佈在不同的伺服器上,以此來降低系統對單主節點的依賴,從而提高 Redis 服務的讀寫效能。

image.png

面試官:

使用哨兵模式在資料上有副本資料做保證,在可用性上又有哨兵監控,一旦master當機會選舉salve節點為master節點,這種已經滿足了我們的生產環境需要,那為什麼還需要使用叢集模式呢?

小張:

額,哨兵模式歸根節點還是主從模式,在主從模式下我們可以通過增加salve節點來擴充套件讀併發能力,但是沒辦法擴充套件寫能力和儲存能力,儲存能力只能是master節點能夠承載的上限。所以為了擴充套件寫能力和儲存能力,我們就需要引入叢集模式。

面試官:

叢集中那麼多Master節點,redis cluster在儲存的時候如何確定選擇哪個節點呢?

小張:

這應該是使用了某種hash演算法,但是我不太清楚。。。

image.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原理的這些面試點你們記住了嗎?希望你們的面試不會被這個問題難倒喲~

相關文章