大家好,我是 31 歲的小米,一個寫代碼十幾年、踩坑踩成見坑就想講故事的技術 UP 主。今天這篇文章的靈感來自上週一個社招 Java 面試候選人。他坐在我對面,手心微微冒汗,卻盯着我一臉篤定地回答:“Redis 是單線程的,所以用不上多核 CPU,這就是 Redis 慢的原因。”

我愣了三秒。Redis 慢?Redis 覺得自己受到侮辱。

我當場把水杯放下,對他説:“兄弟,你這是侮辱 Redis 祖墳級別的優化哲學啊。”

於是,我決定把那天和候選人的對話寫成故事,也順便給所有準備面試的同學講明白:

Redis 為什麼是單線程?單線程怎麼做到這麼快?明明單線程,為什麼也能吃滿多核 CPU?我們 Java 程序員又該怎麼在大廠面試裏把這個題答到面試官心窩子裏?

來,故事開始。

那天的面試:候選人説 Redis 利用不了多核,我的嘴角狂抖

那天候選人説完那句話後,我問他:“那你知道 Redis 為啥單線程還這麼快嗎?”

候選人撓撓頭,説:“因為它用的是 C 語言?”

我當場心裏默默扣分 10 分。於是我把紙筆推給他,讓他畫畫 Redis 的執行模型。

他畫了幾根橫線,説:“單線程啊,能畫什麼?”

我又默默扣了 10 分。如果你也覺得“Redis 單線程 = 性能差 = 用不上多核”,那我必須給你補這一課。

Redis 為什麼單線程還能這麼快?因為它把單線程優化到了極致

我跟候選人説:“Redis 單線程不代表 Redis 整個進程只有一個線程。”

候選人:???

我繼續解釋:“Redis 單線程只是指 網絡 IO 和命令執行是單線程的。但 Redis 本身不是隻有一個線程,比如持久化線程、集羣通信線程、lazy-free 異步刪除線程等等。”

但這不是重點。重點是:

Redis 單線程仍然可以打爆多線程數據庫,是因為:

  1. 純內存操作,速度極快
  2. IO 多路複用,單線程也能處理海量併發
  3. 避免鎖競爭,沒有上下文切換的開銷
  4. 命令執行路徑短、純 C 的極致優化

換句話説:

Redis 單線程並不是因為簡單,而是因為極致。Redis 不是不想多線程,是覺得多線程會拖慢它。

我問候選人:“你知道上下文切換是什麼嗎?”

他:知道,會切來切去浪費 CPU。

我點點頭:“對,所以 Redis 創始人 antirez 選擇單線程,其實是選擇性能。”

説到這裏,候選人終於有點恍然大悟。

但重點來了:那 Redis 單線程,怎麼吃掉多核 CPU?

候選人接着問我:“那 Redis 既然是單線程,那我服務器 16 核豈不是浪費了?”

我笑了。

事實是:大多數互聯網公司的 Redis 集羣,可以幹到全服 CPU 打滿(當然是假設你 QPS 很高的情況下)。

怎麼做到的?

方法一:一台機器部署多個 Redis 實例,每個實例綁定不同 CPU 核心

我告訴候選人:“大廠都會這樣搞。”

舉個例子:

  • 服務器:16 核 64G
  • 部署:8 個 Redis 實例
  • 每個實例綁定一個獨立 CPU 核心
  • 實例之間用不同端口運行,如:6379、6380、6381……6386

結果是:

單線程實例 × 多核 CPU = 多實例並行

這就像 8 個高速收費站,不會出現所有車都擠一條道。候選人聽到這裏已經開始瘋狂點頭。我繼續補刀:

為什麼企業喜歡這種方式?

  1. 提升多核利用率
  2. 一個實例爆掉不影響另外的實例,隔離性強
  3. 可以根據業務拆分成多個 Redis 實例(訂單 Redis、用户緩存 Redis、商品緩存 Redis…)
  4. 便於擴容,可靈活遷移

説白了,就是:

Redis 單線程不等於 Redis 只能用一個核,而是你應該多開幾個 Redis。

Redis 官方也認可這種方式。

方法二:使用 Redis Cluster,讓不同分片分散在多個 Redis 實例上

我跟候選人説:“如果你只開一個 Redis,那麼你確實不能用滿多核。但是你建一個 Redis Cluster 呢?”

Redis Cluster 有 16384 個 slot,每個 slot 可以分配給不同的 Redis 節點。比如你有:

  • redis-7001:master(slot:0~5460)
  • redis-7002:master(slot:5461~10922)
  • redis-7003:master(slot:10923~16383)

每個 Redis 節點自己單線程,但是三個節點跑在:

  • CPU 核 1
  • CPU 核 2
  • CPU 核 3

這是不是又利用上多核了?甚至你可以繼續擴容:

  • 一台機器跑多個 Redis 節點
  • 多台機器分佈更合理地使用 CPU

這樣整個集羣的 CPU 就能全部吃滿。

方法三:Redis 7.0 多線程 IO 支持

我問候選人:“你知道 Redis 7.0 開始支持多線程了嗎?”

他:啊?不是一直單線程嗎?

這是大部分 Java 面試者常犯的錯誤。Redis 6.0 起加入多線程,但不是執行命令用多線程,而是:多線程處理網絡 IO:read/write/writev/sendfile

也就是説:

  • 多線程處理網絡讀寫
  • 單線程執行命令

結果是:

  1. IO 吞吐提升明顯
  2. 網絡瓶頸被解決
  3. Redis 單核性能更上一層樓

這就叫:

“命令執行單線程,網絡 IO 多線程”

很多人誤解以為 Redis 完整變成多線程了,其實不是。但這個優化讓 Redis 可以利用更多 CPU 核心。

方法四:使用 Pipeline + 批量命令降本增效

然後我問候選人:“你知道為什麼你 Redis CPU 只佔 5% 嗎?”

他:因為 Redis 太快?

我説:“不是,是因為你的應用太慢。”大部分 Java 系統訪問 Redis 時:

  1. 一個請求只查一次 Redis
  2. 每次查 Redis 都走一次網絡消耗
  3. QPS 低到 Redis 根本不屑用 CPU

如果你用 Pipeline:

  • 一次發 20 條命令
  • 一次返回結果

你的 Redis 負載立刻提高,CPU 使用率上升。Redis 都等着你 Java 應用多來點請求。

方法五:用 Lua 腳本降低命令往返次數

我繼續補充:

如果你把 10 個操作組合成 1 個 Lua 腳本執行:

  • Redis 只處理一次命令
  • IO 往返都減少
  • CPU 也能利用得更高

例如做搶購、庫存扣減時,Lua 讓 Redis 執行路徑更短、效率更高。

那天的面試後,候選人突然懂了:Redis 單線程不等於只能用一個核!

我最後問他一個終極問題:

如果你是 Redis 作者,你會把執行命令做好多線程嗎?

候選人陷入沉思。幾秒後,他説了一句讓我很欣慰的話:

“如果 Redis 執行命令多線程,鎖競爭、上下文切換、數據一致性都會變複雜,也會變慢……”

我點頭。Redis 作者早就説過:

“多線程不是 Redis 的問題解決方案,Redis 的核心是降低延遲。”

如果你真的想利用多核:

  • 開多個 Redis 實例
  • 用 Redis 集羣
  • 利用 Redis 6+ 多線程 IO
  • 增加業務 QPS,讓 Redis 忙起來
  • 用 Pipeline、Lua 降低網絡開銷

只要你願意,Redis 可以把你的 CPU 燒到冒煙。

我給所有準備社招 Java 面試的小夥伴的一些建議

如果你在面試裏遇到這個題,千萬不要説 Redis 因為單線程所以慢。面試官喜歡聽的答案是這樣的:

標準滿分回答(建議背熟):

Redis 雖然執行命令是單線程的,但並不是不能利用多核 CPU。

企業通常通過以下方式提升多核利用率:

一機多實例:在一台服務器上部署多個 Redis 實例,並綁定到不同 CPU 核心。

Redis Cluster 分片:不同分片運行在不同實例,從而橫向擴展 CPU。

Redis 6/7 的多線程 IO 優化:雖然命令執行仍為單線程,但 IO 讀寫已多線程,可以提升 CPU 利用率。

通過 Pipeline、Lua 腳本減少網絡往返,提高吞吐,從而讓 Redis 更充分利用 CPU。

因此,Redis 單線程不等於性能差,它通過架構層面的方式實現了對多核 CPU 的高效利用。

面試官最愛問的坑:Redis 明明單線程,為什麼多核能吃滿?_多線程

總結:單線程 Redis 背後的哲學

我們經常誤解“單線程”這個詞。單線程不是落後,而是 Redis 對性能的一種極致選擇。

Redis 用單線程避免了:

  • 複雜鎖
  • 搶佔
  • 上下文切換
  • 多線程共享內存問題

它專注做一件事:在內存裏以最快速度處理網絡請求。

至於多核利用?那是架構層面(多實例、多分片)的事情,不是 Redis 內核的活。

END

希望讀完這篇文章,你能在下一次面試裏自信地講:

“Redis 單線程,是為了更快;利用多核,是為了更強。”

如果你喜歡我這種講故事式的技術文章,點個贊,下次繼續給你講更多面試底層知識,讓你面試拿 offer 拿到手軟。

我是小米,一個喜歡分享技術的31歲程序員。如果你喜歡我的文章,歡迎關注我的微信公眾號“軟件求生”,獲取更多技術乾貨!