【搞定面試官】系列:字節面試官的靈魂三問,爲何Redis這麼快,怎樣支撐高併發,如何解決併發競爭問題

前言


Redis是當前煊赫一時的NoSQL數據庫,幾乎已經成爲高併發、高可用系統的標配了。如果對Redis響應快的認知僅僅停留在基於內存和單線程的層面,恐怕是很難斬獲大廠的Offer。本篇重點全面介紹Redis的單線程模型及如何支撐高併發的。
面試

1 Redis徹底基於內存


Redis可以支撐高併發的第一個重要的緣由就是徹底基於內存,這在很大程度上避免了諸如MySQL等關係型數據庫須要頻繁的磁盤I/O(磁盤I/O的尋道時間、旋轉延遲、傳輸時間等很耗時,皆是ms級的開銷,而基於內存基本都是ns級的開銷,徹底不是一個數量級)。
redis

2 Redis的線程模型


2.1 阻塞式I/O模型Blocking I/O

說到I/O模型,你們必定能想到傳統意義上的Blocking I/O,該模型的工做方式是:當對某一個文件描述符(File Descriptor,如下簡稱FD)讀寫時,若是當前FD不可讀或不可寫,則不會對其它進程的操做作出響應,致使總體服務不可用。算法

文件描述符:在形式上是一個非負整數。實際上它指向內核爲每個進程所維護的該進程打開文件的記錄表。

系統架構設計 (1).png

2.2 I/O多路複用

在 I/O 多路複用模型中,最重要的函數調用就是 select,該方法的可以同時監控多個文件描述符的可讀可寫狀況,當其中的某些文件描述符可讀或者可寫時,select 方法就會返回可讀以及可寫的文件描述符個數。
系統架構設計 (1).png
數據庫

2.3 Redis的單線程模型

Redis內部使用文件事件處理器File event handler,該處理器是單線程的,因此Redis才叫作單線程模型。它採用 I/O 多路複用機制同時監聽多個 Socket,根據 Socket上的事件來選擇對應的事件處理器進行處理。Redis服務採用Reactor的方式來實現文件事件處理器,以下圖所示:
系統架構設計 (3).png
綜上分析,Redis支撐高併發的第二個緣由就是基於非阻塞式的I/O多路複用機制,是單線程模型,這在很大程度上避免了多線程頻繁上下文切換的消耗。
數組

3 Redis豐富靈活的數據類型


Redis提供了string、hash、list、set、sorted set五種基本數據結構,而且還包含了HyperLogLog、Pub/Sub等多種擴展的優秀數據結構。
Redis中數據結構不只僅是豐富,閱讀源碼你還會發現,設計也很巧妙很靈活,如string底層不一樣於C語言的實現,而是經過一種SDS(Simple Dynamic String)結構實現的,本質上是一個帶長度信息的字節數組。這裏少俠以字典hash爲例,介紹Redis是如何作到快速響應的。
數據結構

3.1 hash內部結構

Redis中的hash和Java中的HashMap幾乎同樣,都是經過分桶的方式存儲元素並解決hash衝突,第一維是數組,第二維是鏈表,以下圖所示,數組中保存的是鏈表中第一個元素的指針。
image.png多線程

3.2 hash函數的選擇

在介紹Redis的hash函數前,少俠想先引入一個概念:hash攻擊,簡而言之,就是由於hash函數具備偏向性,黑客會根據這種偏向性進行攻擊,致使hash的鏈表長度極不均勻,甚至全部的元素都集中到一個鏈表,最終會致使查詢性能急劇降低(從O(1)退化到O(n))。

那麼Redis如何設計hash函數的呢?不一樣於Java中的hash算法,Redis實際上採用了一種稱爲siphash算法,這個算法是兼顧了隨機性和性能的結果。
架構

3.3 漸進式rehash

說到hash,不可避免的就是擴容,可是Redis是單線程的,而且一些熱點數據頻繁被請求,若是此時擴容Redis很難承受這樣的耗時過程。因此Redis採用了漸進式rehash策略。
漸進式rehash會在rehash的同時,保留新舊兩個hash結構,以下圖所示,查詢時會同時查詢兩個hash結構,而後會在操做指令(hget,hdel)中實現數據遷移,可是若是客戶端一直沒有命令來觸發此類操做,Redis會在定時任務中主動對hash進行數據遷移。
image.png併發

4 Redis如何解決併發競爭問題


  • 客戶端加鎖(ReentrantLock或synchronized),但此方式只限於單機加鎖,沒法解決分佈式系統的併發競爭問題。
  • 樂觀鎖(redis 的命令 watch):
    當執行多鍵值事務操做時,Redis 不只要求這些鍵值須要落在同一個 Redis 實例上,還要求落在同一個 slot 上,因此 redis 的事務比較雞肋,不過能夠想辦法遵循 redis 內部的分片算法把設計到的全部 key 分到同一個 slot。
  • redis 的 setnx 實現分佈式鎖:
    要設置超時時間,防止搶佔到鎖的客戶端因失敗、崩潰或其餘緣由沒有辦法釋放鎖而形成死鎖。

小結


Redis爲什麼這麼快實際上是個涉及面很普遍的問題,面試者若是僅僅停留在基於內存和多路IO複用這些概念層的理解,在面試中是要吃虧的。熟練運用,深刻原理才能在面試中斬獲佳績。分佈式

我是少俠露飛,熱愛技術,熱愛分享。