Redis 是什麼:不只是快取,是記憶體資料結構伺服器

· tech

#redis#concept

📑 目錄

大多數人第一次認識 Redis,都是把它當「快取」——把資料庫查詢的結果丟進去、下次直接拿。這沒錯,但也把它看小了。Redis 的本質是一台記憶體資料結構伺服器(in-memory data structure server),快取只是它眾多用途裡最出名的一個。這篇講清楚它到底是什麼、為什麼這麼快、以及什麼時候該用、什麼時候別用。

不只是快取:它的 value 是「資料結構」

Redis 跟 memcached 這類純 key-value 快取最根本的差別,在於 value 是什麼。傳統快取的 value 是一坨對伺服器不透明的字串(blob),你要改其中一個欄位,得把整包讀回應用端、改完再整包寫回去。Redis 不一樣:它的 value 可以是 List、Hash、Set、Sorted Set,而且伺服器端原生支援對這些結構的操作——你能直接叫它 push 一個元素、幫某個成員加分、取出排名前十,全部在伺服器端原子完成:

傳統 KV 快取(memcached) key → 「一坨字串」(不透明 blob) 改一個欄位 = GET 整包 → 應用端改 → SET 整包 Redis(資料結構伺服器) key → List / Hash / Set / ZSet 伺服器端直接操作(原子)= HSET · LPUSH · ZADD · ZRANGE 例:排行榜 = 一個 Sorted Set,ZADD 記分 + ZRANGE 取前 N 不必把整個榜撈回應用端自己排序——運算搬到資料旁邊做,又快又原子
這是認識 Redis 最重要的一次觀念升級:它不是「存字串的快取」,而是「可以遠端操作的資料結構」。傳統快取要改一個欄位得整包搬進搬出;Redis 讓你把運算(排序、計數、集合運算、範圍查詢)直接推到資料旁邊、在伺服器端原子完成。排行榜、限流、佇列、去重統計——這些用 Redis 幾行命令就搞定的事,底層都是這個差別在發威

換個角度說:Redis 給你的,是一個共享的、超快的、可以遠端操作的資料結構工具箱。多個服務可以同時對同一個 Sorted Set 記分、對同一個 Set 做去重、對同一個 List 收發任務——把原本要在單一程式裡才有的資料結構,變成整個分散式系統都能共用的東西。這才是它真正的價值,快取只是其中一格。

為什麼這麼快

Redis 動輒單機十萬級 QPS、微秒級延遲。快的原因不是單一魔法,而是三件事疊在一起:

為什麼 Redis 這麼快:三件事疊起來 ① 記憶體 資料全在 RAM 免磁碟隨機 I/O (比 DB 快好幾個 數量級) ② 單執行緒 + 免鎖 命令一個一個跑 無鎖競爭 / 無切換 簡單、可預測 (下一篇深入) ③ I/O 多工(epoll) 一個執行緒 服務上萬並行連線 不用一連線一 thread + 省記憶體的內部編碼(ziplist / intset…) 結果:微秒級延遲、單機十萬級 QPS
三根支柱缺一不可:記憶體拿掉了最慢的磁碟隨機 I/O;單執行緒換來的是「無鎖、無 race、行為可預測」的簡單(這也是為什麼一個跑太久的命令會拖垮整台——下一篇的主題);epoll I/O 多工讓單一執行緒就能同時招呼上萬條連線。再加上針對小資料量身打造的省記憶體編碼,才湊出那個誇張的效能數字

這裡有個常被忽略的重點:單執行緒不是缺點,是刻意的設計取捨。它用「一次只做一件事」換來了整個系統的簡單與可預測——沒有鎖、沒有競態、命令的原子性天生成立。代價則是:一個慢命令會卡住所有人(因為大家排同一條隊)。這個一體兩面,是下一篇的主角。

什麼時候用、什麼時候別用

把 Redis 當「資料結構工具箱」之後,適用場景就很好判斷了:

  • 很適合:快取、session 儲存、排行榜(ZSet)、計數器與限流、輕量任務佇列(List / Stream)、去重與基數統計(Set / HyperLogLog)、即時排名、分散式鎖、pub/sub 通知。共通點是——熱、小、要快、且能對應到某個資料結構
  • 別硬塞:當主資料庫存大量冷資料(記憶體很貴)、存超大的單一 value、需要複雜的多表 join 與關聯查詢、或需要金融級的強持久化與交易保證。這些是關聯式資料庫或專用系統的地盤,Redis 不是拿來取代它們的。

一句話:Redis 負責「熱且需要即時操作」的那一小塊,把最燙的資料與運算扛下來;冷的、大的、要強一致的,交給後面的資料庫。

動手:感受「操作結構」而不是「存 blob」

我們畢竟是工程師,連上 redis-cli 玩一下最有感——重點不是命令多,而是你操作的是結構本身:

redis-cli                     # 連上(預設 127.0.0.1:6379,-h/-p/-a 指定主機/埠/密碼)
> SET user:1 "Aidan"          # String
> LPUSH feed p3 p2 p1         # List:直接往頭塞,不必把整包讀回來改再寫回
> HSET user:1 age 30 city TP  # Hash:只改一個欄位
> INFO server                 # 版本、執行模式、連線數
> DBSIZE                      # 現在幾個 key

看那句 LPUSH——你沒有「讀出整個 list、改完寫回」,而是直接對結構下一個操作。這就是「記憶體資料結構伺服器」跟 memcached 存一坨 blob 的根本差別,也是後面每一篇的地基。

反思

把 Redis 當「資料結構」而不是「快取」,用法會完全不同

我看過很多人用 Redis,永遠只有 GET/SET 兩招——把它當一個快一點的 memcached。這其實浪費了它九成的能力。真正的轉捩點,是有次做即時排行榜:我原本打算把分數存 DB、每次查詢時 ORDER BY 撈回來排,壓力測試直接跪。後來改成一個 Sorted Set,ZADD 記分、ZREVRANGE 取榜,程式碼少一半、延遲從幾百毫秒掉到個位數毫秒。那一刻我才真的懂:Redis 的威力不在「快取」,在於它把資料結構變成一個共享的、遠端可操作的服務。 從此我看需求的角度變了——不是「這要不要快取」,而是「這對應到哪個資料結構、能不能把運算推到 Redis 端做」。

快是有代價的,而代價決定了它的邊界

Redis 快得誇張,但工程上沒有白吃的午餐,它的每一個「快」都對應一個「不能」。記憶體快,但記憶體貴又有限——所以它適合熱資料、不適合當大倉庫。單執行緒簡單又免鎖,但一個慢命令會卡死全場——所以你得對 KEYS、對大集合的 O(N) 操作保持警惕。我現在評估要不要用 Redis,想的不只是「它能不能做到」,而是「它的代價落在哪、我承不承受得起」。看懂一個工具的取捨,比看懂它的功能更重要——因為功能決定它能幹嘛,取捨決定它什麼時候會咬你。

它是「熱資料層」,不是資料庫的替代品

最後一個我常提醒團隊的觀念:Redis 是熱資料層,坐在應用和資料庫之間,扛下最燙、最需要即時操作的那一小塊,而不是拿來取代後面那個「真相來源」。把它的定位擺正,很多架構問題會自己消失:資料的權威副本仍在資料庫、Redis 裡的是可重建的加速層,於是「Redis 掛了資料會不會不見」這種焦慮,就從「災難」降級成「回源慢一下」。分清楚誰是真相、誰是加速,是用好任何快取層的第一步——這也是後面幾篇談持久化、談 cluster 時,我們會一再回來的主線。