呼,終於來到這 30 天手寫 Redis Clone 的最後一站了。
最後一天,就把系統監控指令(INFO 與 MONITOR)補起來,再用官方工具壓測一下,看看這 30 天到底寫出了什麼。
一個 server 只會收命令還不夠,至少也要能看目前狀態,例如記憶體用量、連線數、命中率這些資訊。
INFO 命令INFO 用來返回伺服器的統計數據(如版本、活躍從節點個數、當前 Key 數量、AOF 啟用狀態等)。在我們實作的 INFO 中,我們將這些指標格式化為純文字回傳:
# Server
version:1.0.0
os:macOS/Linux
# Clients
connected_clients:1
# Memory
used_memory_keys:5
max_keys_limit:0
# Persistence
aof_enabled:1
rdb_last_save_status:ok
# Replication
role:master
connected_slaves:0
MONITOR 命令MONITOR 是拿來 debug 的工具,可以即時印出 server 收到的每一條命令。實作上是在收到請求時,把命令內容非同步廣播給所有處於 MONITOR 模式的管理者 client,有點像借用 Pub/Sub 的推送概念。
實作這個功能的時候,一開始忘了把回傳訊息格式化,導致 Client 端收到的字串亂七八糟,稍微修改了一下格式才像樣。
這裡用官方提供的 redis-benchmark 跑一下本地壓測,先看 Go 版本大概落在哪個區間。
redis-benchmark -p 6379 -n 100000 -c 50 -q
這會使用 50 個並行客戶端,發送 10 萬個 PING、SET、GET 請求。
在我們的 Go 實作版本(未經分段鎖優化,全域單個 RWMutex,AOF fsync策略為 everysec)的評測結果如下:
PING_INLINE: 98,230.12 requests per second
SET: 85,410.60 requests per second
GET: 89,120.45 requests per second
官方 Redis C 版本大概可以跑到 10 萬到 15 萬 QPS,這個 Go Clone 在還沒做分段鎖和更細緻 I/O 優化的情況下,能接近 9 萬 QPS,比我一開始預期好不少。
不過這也不能說已經追上 Redis 本尊,只能說目前的 RESP parser、KV 引擎和網路層沒有太離譜的瓶頸。
最後收尾前,先確保這 30 天累積的測試都還能過:
$ go test -v ./...
# 預期回覆:
# === RUN TestSetGet
# --- PASS: TestSetGet (0.00s)
# === RUN TestAofRewrite
# --- PASS: TestAofRewrite (0.01s)
# ...
# PASS
# ok redis-clone/db 0.152s
# ok redis-clone/server 0.081s
測試都過後,再跑最後一次效能評測,看目前 QPS 大概落在哪:
$ redis-benchmark -p 6379 -n 100000 -c 50 -q
# 預期回覆:
# PING_INLINE: 99800.39 requests per second, p50=0.250 msec
# SET: 88510.60 requests per second, p50=0.350 msec
# GET: 92120.45 requests per second, p50=0.320 msec
看著逼近十萬的 QPS,這一個月熬夜肝出來的 code 算是滿值得的啦!
回頭看這 30 天,這個 Redis Clone 已經從空專案長成一個真的能跑、也能處理不少核心特性的記憶體資料庫:
Day 01 - 05:網路與協定解析 ── 手寫高併發 TCP Socket、Graceful Shutdown與標準 RESP Parser
Day 06 - 12:記憶體引擎 ── 實作thread-safe KV DB,包含 List, Hash, Set, ZSet (手寫 Skip List)
Day 13 - 18:命令路由 ── 實作 Command Dispatcher、事務排隊 (MULTI/EXEC) 與 Pub/Sub 廣播
Day 19 - 24:持久化 ── 實作非同步 everysec AOF fsync、AOF背景重寫、Gob二進位 RDB 快照
Day 25 - 30:進階特性 ── 實作 LRU/LFU 淘汰演算法、SLAVEOF/SYNC 主從複製、AUTH 驗證與統計監控
bufio.Reader 實作二進位安全的序列化協定解析,也順便支援 Pipeline 的連續讀取。手寫 Redis Clone 不只是照著功能列表補 code。真的寫一輪後,才會對資料結構、網路協定、持久化和複製流程的細節更有感。
鐵人賽雖然結束了,但這專案還有很多可以繼續玩的地方,像是分段鎖、Cluster、或更完整的監控指標。這 30 天先到這裡,收工去吃頓好的!