大家好,我是 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 單線程仍然可以打爆多線程數據庫,是因為:
- 純內存操作,速度極快
- IO 多路複用,單線程也能處理海量併發
- 避免鎖競爭,沒有上下文切換的開銷
- 命令執行路徑短、純 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 個高速收費站,不會出現所有車都擠一條道。候選人聽到這裏已經開始瘋狂點頭。我繼續補刀:
為什麼企業喜歡這種方式?
- 提升多核利用率
- 一個實例爆掉不影響另外的實例,隔離性強
- 可以根據業務拆分成多個 Redis 實例(訂單 Redis、用户緩存 Redis、商品緩存 Redis…)
- 便於擴容,可靈活遷移
説白了,就是:
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
也就是説:
- 多線程處理網絡讀寫
- 單線程執行命令
結果是:
- IO 吞吐提升明顯
- 網絡瓶頸被解決
- Redis 單核性能更上一層樓
這就叫:
“命令執行單線程,網絡 IO 多線程”
很多人誤解以為 Redis 完整變成多線程了,其實不是。但這個優化讓 Redis 可以利用更多 CPU 核心。
方法四:使用 Pipeline + 批量命令降本增效
然後我問候選人:“你知道為什麼你 Redis CPU 只佔 5% 嗎?”
他:因為 Redis 太快?
我説:“不是,是因為你的應用太慢。”大部分 Java 系統訪問 Redis 時:
- 一個請求只查一次 Redis
- 每次查 Redis 都走一次網絡消耗
- 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 內核的活。
END
希望讀完這篇文章,你能在下一次面試裏自信地講:
“Redis 單線程,是為了更快;利用多核,是為了更強。”
如果你喜歡我這種講故事式的技術文章,點個贊,下次繼續給你講更多面試底層知識,讓你面試拿 offer 拿到手軟。
我是小米,一個喜歡分享技術的31歲程序員。如果你喜歡我的文章,歡迎關注我的微信公眾號“軟件求生”,獲取更多技術乾貨!