Redis 作為全球最受歡迎的記憶體資料庫,以單機超過 10 萬 QPS 的恐怖吞吐量與微秒級(μs)極致延遲著稱。

然而,在現代多核心 CPU 當道的時代,許多工程師心中都有一個巨大的疑問:「為什麼 Redis 核心執行緒只採用單執行緒(Single-threaded)架構?單執行緒為何能做到如此極速?」

到了 Redis 6.0,官方又為何引入了 多執行緒(Threaded I/O)?這是否打破了其原有的單執行緒原子性保證?

本文將從作業系統底層、事件迴圈(Event Loop)、內部資料結構到多執行緒網路 I/O,全面解密 Redis 的核心架構。


1. 為什麼 Redis 採用單執行緒卻能做到極速?

Redis 的單執行緒特指**「網路請求解析、命令執行與資料讀寫」** 由單一主執行緒完成(其背景仍有非同步執行緒負責 AOF 刷盤、UNLINK 惰性釋放記憶體等)。

1.1 三大效能核心支柱

  1. 純記憶體操作(Pure In-Memory):所有資料均駐留在記憶體中,資料讀寫耗時在數十至數百奈秒(ns)級別,完全沒有磁碟 I/O 尋道延遲。
  2. 非阻塞 I/O 多路復用(I/O Multiplexing):基於 Linux epoll,單一執行緒可同時監聽數萬個 Socket 連線,僅在有資料可讀寫時才被喚醒。
  3. 完全消除了執行緒切換與鎖競爭開銷:
    • 多執行緒模型在處理高併發共享資料時,必須依賴互斥鎖(Mutex)、讀寫鎖或自旋鎖來保證線程安全,這會引發嚴重的鎖競爭(Lock Contention) 與作業系統上下文切換(Context Switch)。
    • Redis 主執行緒無鎖運行,每個指令天然具備絕對的原子性(Atomicity)。

2. Redis 事件驅動核心:aeEventLoop 反應器架構

Redis 自研了一套極其精簡的高效能事件驅動庫 ae(A simple event-driven library):

Redis aeEventLoop 反應器核心事件循環架構圖展示 aeMain 呼叫 epoll 監聽檔案事件派發業務命令執行,並處理 serverCron 時間事件。aeMain() 核心主事件循環aeApiPoll() (Linux epoll_wait I/O 多路復用)1. 檔案事件 (File Events)讀取 Socket ➔ 執行 GET/SET ➔ 寫入回應 Buffer2. 時間事件 (serverCron)過期鍵清除、主從複製重連、漸進式 Rehash

3. Redis 核心資料結構底層黑科技

Redis 外部封裝的 String、Hash、List、Set、ZSet,在底層由一系列極致優化空間與效能的資料結構組成:

外部資料型態底層資料結構 (Encoding)核心設計亮點
1. StringSDS (Simple Dynamic String)O(1) 長度獲取、二進位安全、空間預分配
2. HashListPack (緊湊列表) / Dict (字典)小資料時連續記憶體,大資料轉哈希表
3. ListQuickList (雙向鏈表 + ListPack)兼顧鏈表插入彈性與連續記憶體快取友善
4. SetIntSet (整數集合) / Dict (字典)純整數極致二進位壓縮 (節省70%空間)
5. ZSet (有序集合)ListPack / Dict + SkipList (跳躍表)O(log N) 極速多維範圍檢索與分值排序

3.1 SDS(簡單動態字串)

傳統 C 語言以 \0 結尾的字串存在安全與效能問題。SDS 引入結構體:

  • 內建 len(記錄長度,O(1) 獲取)與 alloc(分配容量)。
  • 空間預分配與惰性釋放:大幅減少字串頻繁拼接修改時的 malloc 記憶體重分配次數。
  • 二進位安全:允許內容包含任意二進位資料(包含 \0,可直接存圖片或 Protobuf 序列化物件)。

3.2 Dict 漸進式 Rehash(Progressive Rehashing)

當 Hash 表中的元素過多或過少時,需要擴容或縮容:

  • Redis 維護兩個哈希表 ht[0] 與 ht[1]。
  • 避免單次百萬級 Rehash 卡死:Redis 不會一次性將 ht[0] 搬移到 ht[1],而是在每次後續的客戶端讀寫請求中,順手將 ht[0] 上索引為 rehashidx 的桶資料搬移一個槽位到 ht[1]。
  • 同時在定時任務 serverCron 中每次花費 1ms 進行輔助搬移,將龐大的計算開銷均攤到每一次日常請求中!

3.3 SkipList(跳躍表)

ZSet 在大資料量時採用跳躍表。相較於紅黑樹或 AVL 樹,跳躍表透過多層隨機高度索引節點(Level 1~32),以更簡單的代碼邏輯實現了 O(log N) 的插入、刪除與範圍掃描(ZRANGEBYSCORE),且在範圍遍歷時效率遠超平衡樹。


4. Redis 6.0+ 多執行緒網路 I/O 演進

在萬兆網卡普及的現代環境中,單一執行緒讀寫網路 Socket 的速度已經趕不上網路傳輸速度。

4.1 多執行緒架構邊界

Redis 6.0+ 多執行緒網路 I/O 與單執行緒核心執行架構圖展示 Client Sockets 經多個 I/O Threads 平行讀取與協議解析,再由 Main Thread 單執行緒原子執行業務指令,最後由 I/O Threads 平行回寫 Socket。Client Sockets (海量並發請求抵達)I/O Threads (多執行緒平行讀取 Socket 與協議解析)Main Thread (核心主執行緒 ➔ 100% 維持單執行緒原子執行!)I/O Threads (多執行緒平行回寫 Socket 輸出至客戶端)
  • 核心原則不變:所有命令的執行依然在單一主執行緒中完成,因此原有命令的原子性保證與無鎖特性 100% 得到保留!
  • 突破極限:僅將最耗費 CPU 週期與系統呼叫的「網路 Socket 讀取、協議解析、網路 Socket 寫入」交由多執行緒平行處理,使 Redis 吞吐量再次獲得 2 倍以上的翻倍暴增!