<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          應(yīng)對面試你要掌握 Redis 哪些原理

          共 5614字,需瀏覽 12分鐘

           ·

          2021-06-13 02:26


          • 案例背景

          • 案例分析

          • 案例解答

            • Redis 如何實現(xiàn)數(shù)據(jù)不丟失?

            • AOF 日志是如何實現(xiàn)的?

            • AOF 執(zhí)行過程

            • 那么 RDB 快照是如何實現(xiàn)的呢?

            • RDB 做快照時會阻塞線程嗎?

            • RDB 做快照的時候數(shù)據(jù)能修改嗎?

            • Redis 如何實現(xiàn)服務(wù)高可用?

          • 總結(jié)


          提及緩存,就不得不提 Redis,Redis 已經(jīng)是現(xiàn)在使用最廣泛的緩存中間件了

          案例背景

          我們現(xiàn)在就模擬一場面試,假如我是面試官,你是候選人,我問你:

          Redis 屬于單線程還是多線程?

          這道題其實就在考察 Redis 的線程模型(這幾乎是 Redis 必問的問題之一)。

          案例分析

          很多人基本都知道 Redis 是單線程的,并且能說出 Redis 單線程的一些優(yōu)缺點,比如,實現(xiàn)簡單,可以在無鎖的情況下完成所有操作,不存在死鎖和線程切換帶來的性能和時間上的開銷,但同時單線程也不能發(fā)揮多核 CPU 的性能。

          很明顯,如果你停留在上面的回答思路上,只能勉強(qiáng)及格,因為對于這樣一道經(jīng)典的面試題,你回答得沒有亮點,幾乎喪失了機(jī)會。一個相對完整的思路應(yīng)該基于 Redis 單線程,補充相關(guān)的知識點,比如:

          Redis 只有單線程嗎?

          Redis 是單線程的,主要是指 Redis 的網(wǎng)絡(luò) I/O 線程,以及鍵值的 SET 和 GET 等讀寫操作都是由一個線程來完成的。但 Redis 的持久化、集群同步等操作,則是由另外的線程來執(zhí)行的。

          Redis 采用單線程為什么還這么快?

          一般來說,單線程的處理能力應(yīng)該要比多線程差才對,但為什么 Redis 還能達(dá)到每秒數(shù)萬級的處理能力呢?主要有如下幾個原因。

          首先,一個重要的原因是,Redis 的大部分操作都在內(nèi)存中完成,并且采用了高效的數(shù)據(jù)結(jié)構(gòu),比如哈希表和跳表。

          其次,因為是單線程模型避免了多線程之間的競爭,省去了多線程切換帶來的時間和性能上的開銷,而且也不會導(dǎo)致死鎖問題。

          最后,也是最重要的一點, Redis 采用了 I/O 多路復(fù)用機(jī)制處理大量的客戶端 Socket 請求,這讓 Redis 可以高效地進(jìn)行網(wǎng)絡(luò)通信,因為基于非阻塞的 I/O 模型,就意味著 I/O 的讀寫流程不再阻塞。

          但是因為 Redis 不同版本的特殊性,所以對于 Redis 的線程模型要分版本來看。

          Redis 4.0 版本之前,使用單線程速度快的原因就是上述的幾個原因;

          Redis 4.0 版本之后,Redis 添加了多線程的支持,但這時的多線程主要體現(xiàn)在大數(shù)據(jù)的異步刪除功能上,例如 unlink key、flushdb async、flushall async 等。

          Redis 6.0 版本之后,為了更好地提高 Redis 的性能,新增了多線程 I/O 的讀寫并發(fā)能力,但是在面試中,能把 Redis 6.0 中的多線程模型回答上來的人很少,如果你能在面試中補充 Redis 6.0 多線程的原理,勢必會增加面試官對你的認(rèn)可。

          你可以在面試中這樣補充:

          雖然 Redis 一直是單線程模型,但是在 Redis 6.0 版本之后,也采用了多個 I/O 線程來處理網(wǎng)絡(luò)請求,這是因為隨著網(wǎng)絡(luò)硬件的性能提升,Redis 的性能瓶頸有時會出現(xiàn)在網(wǎng)絡(luò) I/O 的處理上,所以為了提高網(wǎng)絡(luò)請求處理的并行度,Redis 6.0 對于網(wǎng)絡(luò)請求采用多線程來處理。但是對于讀寫命令,Redis 仍然使用單線程來處理。

          從圖中,你可以看到,線程模型只是高性能考點中的一環(huán),而高可用考點中,包括了持久化、數(shù)據(jù)復(fù)制(主從復(fù)制,哨兵復(fù)制)等內(nèi)容。

          Redis 是如何實現(xiàn)數(shù)據(jù)不丟失的(考察持久化)?

          Redis 是如何實現(xiàn)服務(wù)高可用的(考察數(shù)據(jù)復(fù)制)?

          案例解答

          Redis 如何實現(xiàn)數(shù)據(jù)不丟失?

          我們知道,緩存數(shù)據(jù)庫的讀寫都是在內(nèi)存中,所以它的性能才會高,但在內(nèi)存中的數(shù)據(jù)會隨著服務(wù)器的重啟而丟失,為了保證數(shù)據(jù)不丟失,要把內(nèi)存中的數(shù)據(jù)存儲到磁盤,以便緩存服務(wù)器重啟之后,還能夠從磁盤中恢復(fù)原有的數(shù)據(jù),這個過程就是 Redis 的數(shù)據(jù)持久化。

          這也是 Redis 區(qū)別于其他緩存數(shù)據(jù)庫的優(yōu)點之一(比如 Memcached 就不具備持久化功能)。Redis 的數(shù)據(jù)持久化有三種方式。

          • AOF 日志(Append Only File,文件追加方式):記錄所有的操作命令,并以文本的形式追加到文件中。

          • RDB 快照(Redis DataBase):將某一個時刻的內(nèi)存數(shù)據(jù),以二進(jìn)制的方式寫入磁盤。

          • 混合持久化方式:Redis 4.0 新增了混合持久化的方式,集成了 RDB 和 AOF 的優(yōu)點。

          接下來我們看一下這三種方式的實現(xiàn)原理。

          AOF 日志是如何實現(xiàn)的?

          通常情況下,關(guān)系型數(shù)據(jù)庫(如 MySQL)的日志都是“寫前日志”(Write Ahead Log, WAL),也就是說,在實際寫數(shù)據(jù)之前,先把修改的數(shù)據(jù)記到日志文件中,以便當(dāng)出現(xiàn)故障時進(jìn)行恢復(fù),比如 MySQL 的 redo log(重做日志),記錄的就是修改后的數(shù)據(jù)。

          而 AOF 里記錄的是 Redis 收到的每一條命令,這些命令是以文本形式保存的,不同的是,Redis 的 AOF 日志的記錄順序與傳統(tǒng)關(guān)系型數(shù)據(jù)庫正好相反,它是寫后日志,“寫后”是指 Redis 要先執(zhí)行命令,把數(shù)據(jù)寫入內(nèi)存,然后再記錄日志到文件。

          AOF 執(zhí)行過程

          那么面試的考察點來了:Reids 為什么先執(zhí)行命令,在把數(shù)據(jù)寫入日志呢?為了方便你理解,我整理了關(guān)鍵的記憶點:

          • 因為 ,Redis 在寫入日志之前,不對命令進(jìn)行語法檢查;

          • 所以,只記錄執(zhí)行成功的命令,避免了出現(xiàn)記錄錯誤命令的情況;

          • 并且,在命令執(zhí)行完之后再記錄,不會阻塞當(dāng)前的寫操作。

          當(dāng)然,這樣做也會帶來風(fēng)險(這一點你也要在面試中給出解釋)。

          • 數(shù)據(jù)可能會丟失:如果 Redis 剛執(zhí)行完命令,此時發(fā)生故障宕機(jī),會導(dǎo)致這條命令存在丟失的風(fēng)險。

          • 可能阻塞其他操作:雖然 AOF 是寫后日志,避免阻塞當(dāng)前命令的執(zhí)行,但因為 AOF 日志也是在主線程中執(zhí)行,所以當(dāng) Redis 把日志文件寫入磁盤的時候,還是會阻塞后續(xù)的操作無法執(zhí)行。

          又因為 Redis 的持久化離不開 AOF 和 RDB,所以我們就需要學(xué)習(xí) RDB。

          那么 RDB 快照是如何實現(xiàn)的呢?

          因為 AOF 日志記錄的是操作命令,不是實際的數(shù)據(jù),所以用 AOF 方法做故障恢復(fù)時,需要全量把日志都執(zhí)行一遍,一旦日志非常多,勢必會造成 Redis 的恢復(fù)操作緩慢。

          為了解決這個問題,Redis 增加了 RDB 內(nèi)存快照(所謂內(nèi)存快照,就是將內(nèi)存中的某一時刻狀態(tài)以數(shù)據(jù)的形式記錄在磁盤中)的操作,它即可以保證可靠性,又能在宕機(jī)時實現(xiàn)快速恢復(fù)。

          和 AOF 不同的是,RDB 記錄 Redis 某一時刻的數(shù)據(jù),而不是操作,所以在做數(shù)據(jù)恢復(fù)時候,只需要直接把 RDB 文件讀入內(nèi)存,完成快速恢復(fù)。

          RDB 做快照時會阻塞線程嗎?

          因為 Redis 的單線程模型決定了它所有操作都要盡量避免阻塞主線程,所以對于 RDB 快照也不例外,這關(guān)系到是否會降低 Redis 的性能。

          為了解決這個問題,Redis 提供了兩個命令來生成 RDB 快照文件,分別是 save 和 bgsave。save 命令在主線程中執(zhí)行,會導(dǎo)致阻塞。而 bgsave 命令則會創(chuàng)建一個子進(jìn)程,用于寫入 RDB 文件的操作,避免了對主線程的阻塞,這也是 Redis RDB 的默認(rèn)配置。

          RDB 做快照的時候數(shù)據(jù)能修改嗎?

          這個問題非常重要,考察候選人對 RDB 的技術(shù)掌握得夠不夠深。你可以思考一下,如果在執(zhí)行快照的過程中,數(shù)據(jù)如果能被修改或者不能被修改都會帶來什么影響?

          如果此時可以執(zhí)行寫操作:意味著 Redis 還能正常處理寫操作,就可能出現(xiàn)正在執(zhí)行快照的數(shù)據(jù)是已經(jīng)被修改了的情況;

          如果此時不可以執(zhí)行寫操作:意味著 Redis 的所有寫操作都得等到快照執(zhí)行完成之后才能執(zhí)行,那么就又出現(xiàn)了阻塞主線程的問題。

          那Redis 是如何解決這個問題的呢?它利用了 bgsave 的子進(jìn)程,具體操作如下:

          如果主線程執(zhí)行讀操作,則主線程和 bgsave 子進(jìn)程互相不影響;

          如果主線程執(zhí)行寫操作,則被修改的數(shù)據(jù)會復(fù)制一份副本,然后 bgsave 子進(jìn)程會把該副本數(shù)據(jù)寫入 RDB 文件,在這個過程中,主線程仍然可以直接修改原來的數(shù)據(jù)。

          要注意,Redis 對 RDB 的執(zhí)行頻率非常重要,因為這會影響快照數(shù)據(jù)的完整性以及 Redis 的穩(wěn)定性,所以在 Redis 4.0 后,增加了 AOF 和 RDB 混合的數(shù)據(jù)持久化機(jī)制:把數(shù)據(jù)以 RDB 的方式寫入文件,再將后續(xù)的操作命令以 AOF 的格式存入文件,既保證了 Redis 重啟速度,又降低數(shù)據(jù)丟失風(fēng)險。

          我們來總結(jié)一下,當(dāng)面試官問你“Redis 是如何實現(xiàn)數(shù)據(jù)不丟失的”時,你首先要意識到這是在考察你對 Redis 數(shù)據(jù)持久化知識的掌握程度,那么你的回答思路是:先說明 Redis 有幾種持久化的方式,然后分析 AOF 和 RDB 的原理以及存在的問題,最后分析一下 Redis 4.0 版本之后的持久化機(jī)制。

          Redis 如何實現(xiàn)服務(wù)高可用?

          另外,Redis 不僅僅可以用來當(dāng)作緩存,很多時候也會直接作為數(shù)據(jù)存儲,那么你就要一個高可用的 Redis 服務(wù),來支撐和保證業(yè)務(wù)的正常運行。那么你怎么設(shè)計一個不宕機(jī)的 Redis 高可用服務(wù)呢?

          思考一下,解決數(shù)據(jù)高可用的手段是什么?是副本。那么要想設(shè)計一個高可用的 Redis 服務(wù),一定要從 Redis 的多服務(wù)節(jié)點來考慮,比如 Redis 的主從復(fù)制、哨兵模式,以及 Redis 集群。這三點是你一定要在面試中回答出來的。

          主從同步 (主從復(fù)制)

          這是 Redis 高可用服務(wù)的最基礎(chǔ)的保證,實現(xiàn)方案就是將從前的一臺 Redis 服務(wù)器,同步數(shù)據(jù)到多臺從 Redis 服務(wù)器上,即一主多從的模式,這樣我們就可以對 Redis 做讀寫分離了,來承載更多的并發(fā)操作,這里和 MySQL 的主從復(fù)制原理上是一樣的。

          Redis Sentinel(哨兵模式)

          在使用 Redis 主從服務(wù)的時候,會有一個問題,就是當(dāng) Redis 的主從服務(wù)器出現(xiàn)故障宕機(jī)時,需要手動進(jìn)行恢復(fù),為了解決這個問題,Redis 增加了哨兵模式(因為哨兵模式做到了可以監(jiān)控主從服務(wù)器,并且提供自動容災(zāi)恢復(fù)的功能)。

          Redis Cluster(集群)

          Redis Cluster 是一種分布式去中心化的運行模式,是在 Redis 3.0 版本中推出的 Redis 集群方案,它將數(shù)據(jù)分布在不同的服務(wù)器上,以此來降低系統(tǒng)對單主節(jié)點的依賴,從而提高 Redis 服務(wù)的讀寫性能。

          Redis Cluster 方案采用哈希槽(Hash Slot),來處理數(shù)據(jù)和實例之間的映射關(guān)系。在 Redis Cluster 方案中,一個切片集群共有 16384 個哈希槽,這些哈希槽類似于數(shù)據(jù)分區(qū),每個鍵值對都會根據(jù)它的 key,被映射到一個哈希槽中,具體執(zhí)行過程分為兩大步。

          1. 根據(jù)鍵值對的 key,按照 CRC16 算法計算一個 16 bit 的值。

          2. 再用 16bit 值對 16384 取模,得到 0~16383 范圍內(nèi)的模數(shù),每個模數(shù)代表一個相應(yīng)編號的哈希槽。

          剩下的一個問題就是,這些哈希槽怎么被映射到具體的 Redis 實例上的呢?有兩種方案。

          • 平均分配:在使用 cluster create 命令創(chuàng)建 Redis 集群時,Redis 會自動把所有哈希槽平均分布到集群實例上。比如集群中有 9 個實例,則每個實例上槽的個數(shù)為 16384/9 個。

          • 手動分配:可以使用 cluster meet 命令手動建立實例間的連接,組成集群,再使用 cluster addslots 命令,指定每個實例上的哈希槽個數(shù),為了方便你的理解,我通過一張圖來解釋數(shù)據(jù)、哈希槽,以及實例三者的映射分布關(guān)系。

          示意圖中的分片集群一共有 3 個實例,假設(shè)有 4 個哈希槽時,我們就可以通過命令手動分配哈希槽,比如實例 1 保存哈希槽 0 和 1,實例 2 保存哈希槽 2 和 3。

          然后在集群運行的過程中,key1 和 key2 計算完 CRC16 值后,對哈希槽總個數(shù) 5 進(jìn)行取模,再根據(jù)各自的模數(shù)結(jié)果,就可以被映射到對應(yīng)的實例 1 和實例 3 上了。

          主從同步是 Redis 高可用最基礎(chǔ)的服務(wù)高可用方案,但它當(dāng)發(fā)生故障時,需要手動恢復(fù)故障,因此就有了哨兵模式用于監(jiān)控和實現(xiàn)主從服務(wù)器的自動容災(zāi)。

          總結(jié)

          Redis 的三個核心問題:線程模型、數(shù)據(jù)持久化,以及高可用,分別描述了:

          • 對于線程模型的知識點,你要分開三條線進(jìn)行理解(Redis 4.0 之前、Redis 4.0 之后,以及 Redis 6.0)。

          • 對于數(shù)據(jù)持久化,你要掌握 Redis 持久化的幾種方案,AOF 和 RDB 的原理,以及為了彌補他們的缺點,Redis 增加了混合持久化方式,以較小的性能開銷保證數(shù)據(jù)的可靠性。

          • 實現(xiàn)高可用的三種手段:主從同步、哨兵模式和 Redis 集群服務(wù),對于 Redis 集群,你要掌握哈希槽的數(shù)據(jù)分布機(jī)制,以及自動分配和手動分配的實現(xiàn)原理 。

          — 【 THE END 】—
          本公眾號全部博文已整理成一個目錄,請在公眾號里回復(fù)「m」獲??!

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點“在看”,關(guān)注公眾號并回復(fù) PDF 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  美女裸体91 | 豆花视频免费看 | 大香蕉久久 | 亚洲BBB | 青娱乐亚洲精品视频在线观看 |