# 前言
時間回到 2011 年 4 月, 當時我正在編寫一個用戶關系模塊, 這個模塊需要實現一個“共同關注”功能, 用于計算出兩個用戶關注了哪些相同的用戶。
舉個例子, 假設 huangz 關注了 peter 、tom 、jack 三個用戶, 而 john 關注了 peter 、tom 、bob 、david 四個用戶, 那么當 huangz 訪問 john 的頁面時, 共同關注功能就會計算并打印出類似“你跟 john 都關注了 peter 和 tom ”這樣的信息。
從集合計算的角度來看, 共同關注功能本質上就是計算兩個用戶關注集合的交集, 因為交集這個概念是如此的常見, 所以我很自然地認為共同關注這個功能可以很容易地實現, 但現實卻給了我當頭一棒: 我所使用的關系數據庫并不直接支持交集計算操作, 要計算兩個集合的交集, 除了需要對兩個數據表執行合并(join)操作之外, 還需要對合并的結果執行去重復(distinct)操作, 最終導致交集操作的實現變得異常復雜。
是否存在直接支持集合操作的數據庫呢? 帶著這個疑問, 我在搜索引擎上面進行查找, 并最終發現了 Redis 。 在我看來, Redis 正是我想要找的那種數據庫 —— 它內置了集合數據類型, 并支持對集合執行交集、并集、差集等集合計算操作, 其中的交集計算操作可以直接用于實現我想要的共同關注功能。
得益于 Redis 本身的簡單性, 以及 Redis 手冊的詳盡和完善, 我很快學會了怎樣使用 Redis 的集合數據類型, 并用它重新實現了整個用戶關系模塊: 重寫之后的關系模塊不僅代碼量更少, 速度更快, 更重要的是, 之前需要使用一段甚至一大段 SQL 查詢才能實現的功能, 現在只需要調用一兩個 Redis 命令就能夠實現了, 整個模塊的可讀性得到了極大的提高。
自此之后, 我開始在越來越多的項目里面使用 Redis , 與此同時, 我對 Redis 的內部實現也越來越感興趣, 一些問題開始頻繁地出現在我的腦海中, 比如說:
* Redis 的五種數據類型分別是由什么數據結構實現的?
* Redis 的字符串數據類型既可以儲存字符串(比如?`"hello?world"`?), 又可以儲存整數和浮點數(比如?`10086`?和?`3.14`?), 甚至是二進制位(使用?SETBIT?等命令), Redis 在內部是怎樣儲存這些不同的值的?
* Redis 的一部分命令只能對特定數據類型執行(比如?APPEND?只能對字符串執行,?HSET?只能對哈希表執行), 而另一部分命令卻可以對所有數據類型執行(比如?DEL?、?TYPE?和?EXPIRE?), 不同的命令在執行時是如何進行類型檢查的? Redis 在內部是否實現了一個類型系統?
* Redis 的數據庫是怎樣儲存各種不同數據類型的鍵值對的? 數據庫里面的過期鍵又是怎樣實現自動刪除的?
* 除了數據庫之外, Redis 還擁有發布與訂閱、腳本、事務等特性, 這些特性又是如何實現的?
* Redis 使用什么模型或者模式來處理客戶端的命令請求? 一條命令請求從發送到返回需要經過什么步驟?
為了找到這些問題的答案, 我再次在搜索引擎上面進行查找, 可惜的是這次搜索并沒有多少收獲: Redis 還是一個非常年輕的軟件, 對它的最好介紹就是官方網站上面的文檔, 但是這些文檔主要關注的是怎樣使用 Redis , 而不是介紹 Redis 的內部實現。 另外, 網上雖然有一些博客文章對 Redis 的內部實現進行了介紹, 但這些文章要么并不齊全(只介紹了 Redis 中的少數幾個特性), 要么就寫得過于簡單(只是一些概述性的文章), 要么關注的就是舊版本(比如 2.0 、 2.2 或者 2.4 ,而當時的最新版已經是 2.6 了)。
綜合來看, 詳細而且完整地介紹 Redis 內部實現的資料, 無論是外文還是中文都不存在。 意識到這一點之后, 我決定自己動手注釋 Redis 的源代碼, 從中尋找問題的答案, 并通過寫博客的方式與其他 Redis 用戶分享我的發現。 在積累了七八篇 Redis 源代碼注釋文章之后, 我想如果能將這些博文匯集成書的話, 那一定會非常有趣, 并且我自己也會從中學到很多知識。 于是我在 2012 年年末開始創作《Redis 設計與實現》, 并最終于 2013 年 3 月 8 日在互聯網發布了本書的第一版。
盡管《Redis 設計與實現》第一版順利發布了, 但在我的心目中, 這個第一版還是有很多不完善的地方:
* 比如說, 因為第一版是我邊注釋 Redis 源代碼邊寫的, 如果有足夠時間讓我先完整地注釋一遍 Redis 的源代碼, 然后再進行寫作的話, 那么書本在內容方面應該會更為全面。
* 又比如說, 第一版只介紹了 Redis 的內部機制和單機特性, 但并沒有介紹任何 Redis 多機特性(復制、Sentinel 和集群), 而我認為只有將關于多機特性的介紹也包含進來, 這本《Redis 設計與實現》才算是真正的完成了。
就在我考慮應該何時編寫新版來修復這些缺陷的時候, 機械工業出版社的吳怡編輯來信詢問我是否有興趣正式地出版《Redis 設計與實現》, 能夠正式地出版自己寫的書一直是我夢寐以求的事情, 我找不到任何拒絕這一邀請的理由, 就這樣, 在《Redis 設計與實現》第一版發布幾天之后, 新版《Redis 設計與實現》的寫作也馬不停蹄地開始了。
從 2013 年 3 月到 2014 年 1 月這 11 個月間, 我重新注釋了 Redis 在 unstable 分支的源代碼(也即是現在的 Redis 3.0 源代碼), 重寫了《Redis 設計與實現》第一版已有的所有章節, 并向書中添加了關于二進制位操作(bitop)、排序、復制、Sentinel 和集群等主題的新章節, 最終, 這本新版的《Redis 設計與實現》不僅介紹了 Redis 的內部機制 (比如數據庫實現、類型系統、事件模型), 而且還介紹了大部分 Redis 單機特性 (比如事務、持久化、 Lua 腳本、排序、二進制位操作), 以及所有 Redis 多機特性 (復制、Sentinel 和集群)。
雖然作者創作本書的初衷只是為了滿足自己的好奇心, 但了解 Redis 內部實現的好處并不僅僅在于滿足好奇心: 通過了解 Redis 的內部實現, 理解每一個特性和命令背后的運作機制, 可以幫助我們更高效地使用 Redis , 避開那些可能會引起性能問題的陷阱。 我衷心希望這本新版《Redis 設計與實現》能夠幫助讀者更好地了解 Redis , 并成為更優秀的 Redis 使用者。
本書的第一版獲得了很多熱心讀者的反饋, 這本新版的很多改進也來源于讀者們的意見和建議, 因此我將繼續在 www.RedisBook.com 設置 disqus 論壇(可以不注冊直接發貼), 歡迎讀者隨時就這本新版《Redis 設計與實現》發表提問、意見、建議、批評、勘誤,等等, 我會努力地采納大家的意見, 爭取在將來寫出更好的《Redis 設計與實現》, 以此來回報大家對本書的支持。
黃健宏(huangz)
2014 年 3 月于清遠
- 介紹
- 前言
- 致謝
- 簡介
- 第一部分:數據結構與對象
- 簡單動態字符串
- SDS 的定義
- SDS 與 C 字符串的區別
- SDS API
- 重點回顧
- 參考資料
- 鏈表
- 鏈表和鏈表節點的實現
- 鏈表和鏈表節點的 API
- 重點回顧
- 字典
- 字典的實現
- 哈希算法
- 解決鍵沖突
- rehash
- 漸進式 rehash
- 字典 API
- 重點回顧
- 跳躍表
- 跳躍表的實現
- 跳躍表 API
- 重點回顧
- 整數集合
- 整數集合的實現
- 升級
- 升級的好處
- 降級
- 整數集合 API
- 重點回顧
- 壓縮列表
- 壓縮列表的構成
- 壓縮列表節點的構成
- 連鎖更新
- 壓縮列表 API
- 重點回顧
- 對象
- 對象的類型與編碼
- 字符串對象
- 列表對象
- 哈希對象
- 集合對象
- 有序集合對象
- 類型檢查與命令多態
- 內存回收
- 對象共享
- 對象的空轉時長
- 重點回顧
- 第二部分:單機數據庫的實現
- 數據庫
- 數據庫鍵空間
- 重點回顧
- RDB 持久化
- RDB 文件結構
- 重點回顧
- AOF 持久化
- AOF 持久化的實現
- 重點回顧
- 事件
- 文件事件
- 重點回顧
- 參考資料
- 客戶端
- 客戶端屬性
- 重點回顧
- 服務器
- 命令請求的執行過程
- 重點回顧
- 第三部分:多機數據庫的實現
- 復制
- 舊版復制功能的實現
- 重點回顧
- Sentinel
- 啟動并初始化 Sentinel
- 重點回顧
- 參考資料
- 集群
- 節點
- 重點回顧
- 第四部分:獨立功能的實現
- 發布與訂閱
- 頻道的訂閱與退訂
- 重點回顧
- 參考資料
- 事務
- 事務的實現
- 重點回顧
- Lua 腳本
- 創建并修改 Lua 環境
- 重點回顧
- 排序
- SORT <key> 命令的實現
- 重點回顧
- 二進制位數組
- GETBIT 命令的實現
- 重點回顧
- 慢查詢日志
- 慢查詢記錄的保存
- 慢查詢日志的閱覽和刪除
- 添加新日志
- 重點回顧
- 監視器
- 成為監視器
- 向監視器發送命令信息
- 重點回顧
- 源碼、相關資源和勘誤