Redis 是一個鍵值對(key-value pair)數據庫服務器, 服務器中的每個數據庫都由一個?`redis.h/redisDb`?結構表示, 其中,?`redisDb`?結構的`dict`?字典保存了數據庫中的所有鍵值對, 我們將這個字典稱為鍵空間(key space):
~~~
typedef struct redisDb {
// ...
// 數據庫鍵空間,保存著數據庫中的所有鍵值對
dict *dict;
// ...
} redisDb;
~~~
鍵空間和用戶所見的數據庫是直接對應的:
* 鍵空間的鍵也就是數據庫的鍵, 每個鍵都是一個字符串對象。
* 鍵空間的值也就是數據庫的值, 每個值可以是字符串對象、列表對象、哈希表對象、集合對象和有序集合對象在內的任意一種 Redis 對象。
舉個例子, 如果我們在空白的數據庫中執行以下命令:
~~~
redis> SET message "hello world"
OK
redis> RPUSH alphabet "a" "b" "c"
(integer) 3
redis> HSET book name "Redis in Action"
(integer) 1
redis> HSET book author "Josiah L. Carlson"
(integer) 1
redis> HSET book publisher "Manning"
(integer) 1
~~~
那么在這些命令執行之后, 數據庫的鍵空間將會是圖 IMAGE_DB_EXAMPLE 所展示的樣子:
* `alphabet`?是一個列表鍵, 鍵的名字是一個包含字符串?`"alphabet"`?的字符串對象, 鍵的值則是一個包含三個元素的列表對象。
* `book`?是一個哈希表鍵, 鍵的名字是一個包含字符串?`"book"`?的字符串對象, 鍵的值則是一個包含三個鍵值對的哈希表對象。
* `message`?是一個字符串鍵, 鍵的名字是一個包含字符串?`"message"`?的字符串對象, 鍵的值則是一個包含字符串?`"hello?world"`?的字符串對象。

因為數據庫的鍵空間是一個字典, 所以所有針對數據庫的操作 —— 比如添加一個鍵值對到數據庫, 或者從數據庫中刪除一個鍵值對, 又或者在數據庫中獲取某個鍵值對, 等等, 實際上都是通過對鍵空間字典進行操作來實現的, 以下幾個小節將分別介紹數據庫的添加、刪除、更新、取值等操作的實現原理。
## 添加新鍵
添加一個新鍵值對到數據庫, 實際上就是將一個新鍵值對添加到鍵空間字典里面, 其中鍵為字符串對象, 而值則為任意一種類型的 Redis 對象。
舉個例子, 如果鍵空間當前的狀態如圖 IMAGE_DB_EXAMPLE 所示, 那么在執行以下命令之后:
~~~
redis> SET date "2013.12.1"
OK
~~~
鍵空間將添加一個新的鍵值對, 這個新鍵值對的鍵是一個包含字符串?`"date"`?的字符串對象, 而鍵值對的值則是一個包含字符串?`"2013.12.1"`的字符串對象, 如圖 IMAGE_DB_AFTER_ADD_NEW_KEY 所示。

## 刪除鍵
刪除數據庫中的一個鍵, 實際上就是在鍵空間里面刪除鍵所對應的鍵值對對象。
舉個例子, 如果鍵空間當前的狀態如圖 IMAGE_DB_EXAMPLE 所示, 那么在執行以下命令之后:
~~~
redis> DEL book
(integer) 1
~~~
鍵?`book`?以及它的值將從鍵空間中被刪除, 如圖 IMAGE_DB_AFTER_DEL 所示。

## 更新鍵
對一個數據庫鍵進行更新, 實際上就是對鍵空間里面鍵所對應的值對象進行更新, 根據值對象的類型不同, 更新的具體方法也會有所不同。
舉個例子, 如果鍵空間當前的狀態如圖 IMAGE_DB_EXAMPLE 所示, 那么在執行以下命令之后:
~~~
redis> SET message "blah blah"
OK
~~~
鍵?`message`?的值對象將從之前包含?`"hello?world"`?字符串更新為包含?`"blah?blah"`?字符串, 如圖 IMAGE_DB_UPDATE_CAUSE_SET 所示。

再舉個例子, 如果我們繼續執行以下命令:
~~~
redis> HSET book page 320
(integer) 1
~~~
那么鍵空間中?`book`?鍵的值對象(一個哈希對象)將被更新, 新的鍵值對?`page`?和?`320`?會被添加到值對象里面, 如圖 IMAGE_UPDATE_BY_HSET 所示。

## 對鍵取值
對一個數據庫鍵進行取值, 實際上就是在鍵空間中取出鍵所對應的值對象, 根據值對象的類型不同, 具體的取值方法也會有所不同。
舉個例子, 如果鍵空間當前的狀態如圖 IMAGE_DB_EXAMPLE 所示, 那么當執行以下命令時:
~~~
redis> GET message
"hello world"
~~~
GET?命令將首先在鍵空間中查找鍵?`message`?, 找到鍵之后接著取得該鍵所對應的字符串對象值, 之后再返回值對象所包含的字符串?`"helloworld"`?, 取值過程如圖 IMAGE_FETCH_VALUE_VIA_GET 所示。

再舉一個例子, 當執行以下命令時:
~~~
redis> LRANGE alphabet 0 -1
1) "a"
2) "b"
3) "c"
~~~
LRANGE?命令將首先在鍵空間中查找鍵?`alphabet`?, 找到鍵之后接著取得該鍵所對應的列表對象值, 之后再返回列表對象中包含的三個字符串對象的值, 取值過程如圖 IMAGE_FETCH_VALUE_VIA_LRANGE 所示。

## 其他鍵空間操作
除了上面列出的添加、刪除、更新、取值操作之外, 還有很多針對數據庫本身的 Redis 命令, 也是通過對鍵空間進行處理來完成的。
比如說, 用于清空整個數據庫的?FLUSHDB?命令, 就是通過刪除鍵空間中的所有鍵值對來實現的。
又比如說, 用于隨機返回數據庫中某個鍵的?RANDOMKEY?命令, 就是通過在鍵空間中隨機返回一個鍵來實現的。
另外, 用于返回數據庫鍵數量的?DBSIZE?命令, 就是通過返回鍵空間中包含鍵值對的數量來實現的。
類似的命令還有?EXISTS?、?RENAME?、?KEYS?, 等等, 這些命令都是通過對鍵空間進行操作來實現的。
## 讀寫鍵空間時的維護操作
當使用 Redis 命令對數據庫進行讀寫時, 服務器不僅會對鍵空間執行指定的讀寫操作, 還會執行一些額外的維護操作, 其中包括:
* 在讀取一個鍵之后(讀操作和寫操作都要對鍵進行讀取), 服務器會根據鍵是否存在, 以此來更新服務器的鍵空間命中(hit)次數或鍵空間不命中(miss)次數, 這兩個值可以在?INFO stats?命令的?`keyspace_hits`?屬性和?`keyspace_misses`?屬性中查看。
* 在讀取一個鍵之后, 服務器會更新鍵的 LRU (最后一次使用)時間, 這個值可以用于計算鍵的閑置時間, 使用命令?OBJECT idletime ?命令可以查看鍵?`key`?的閑置時間。
* 如果服務器在讀取一個鍵時, 發現該鍵已經過期, 那么服務器會先刪除這個過期鍵, 然后才執行余下的其他操作, 本章稍后對過期鍵的討論會詳細說明這一點。
* 如果有客戶端使用?WATCH?命令監視了某個鍵, 那么服務器在對被監視的鍵進行修改之后, 會將這個鍵標記為臟(dirty), 從而讓事務程序注意到這個鍵已經被修改過, 《事務》一章會詳細說明這一點。
* 服務器每次修改一個鍵之后, 都會對臟(dirty)鍵計數器的值增一, 這個計數器會觸發服務器的持久化以及復制操作執行, 《RDB 持久化》、《AOF 持久化》和《復制》這三章都會說到這一點。
* 如果服務器開啟了數據庫通知功能, 那么在對鍵進行修改之后, 服務器將按配置發送相應的數據庫通知, 本章稍后討論數據庫通知功能的實現時會詳細說明這一點。
- 介紹
- 前言
- 致謝
- 簡介
- 第一部分:數據結構與對象
- 簡單動態字符串
- SDS 的定義
- SDS 與 C 字符串的區別
- SDS API
- 重點回顧
- 參考資料
- 鏈表
- 鏈表和鏈表節點的實現
- 鏈表和鏈表節點的 API
- 重點回顧
- 字典
- 字典的實現
- 哈希算法
- 解決鍵沖突
- rehash
- 漸進式 rehash
- 字典 API
- 重點回顧
- 跳躍表
- 跳躍表的實現
- 跳躍表 API
- 重點回顧
- 整數集合
- 整數集合的實現
- 升級
- 升級的好處
- 降級
- 整數集合 API
- 重點回顧
- 壓縮列表
- 壓縮列表的構成
- 壓縮列表節點的構成
- 連鎖更新
- 壓縮列表 API
- 重點回顧
- 對象
- 對象的類型與編碼
- 字符串對象
- 列表對象
- 哈希對象
- 集合對象
- 有序集合對象
- 類型檢查與命令多態
- 內存回收
- 對象共享
- 對象的空轉時長
- 重點回顧
- 第二部分:單機數據庫的實現
- 數據庫
- 數據庫鍵空間
- 重點回顧
- RDB 持久化
- RDB 文件結構
- 重點回顧
- AOF 持久化
- AOF 持久化的實現
- 重點回顧
- 事件
- 文件事件
- 重點回顧
- 參考資料
- 客戶端
- 客戶端屬性
- 重點回顧
- 服務器
- 命令請求的執行過程
- 重點回顧
- 第三部分:多機數據庫的實現
- 復制
- 舊版復制功能的實現
- 重點回顧
- Sentinel
- 啟動并初始化 Sentinel
- 重點回顧
- 參考資料
- 集群
- 節點
- 重點回顧
- 第四部分:獨立功能的實現
- 發布與訂閱
- 頻道的訂閱與退訂
- 重點回顧
- 參考資料
- 事務
- 事務的實現
- 重點回顧
- Lua 腳本
- 創建并修改 Lua 環境
- 重點回顧
- 排序
- SORT <key> 命令的實現
- 重點回顧
- 二進制位數組
- GETBIT 命令的實現
- 重點回顧
- 慢查詢日志
- 慢查詢記錄的保存
- 慢查詢日志的閱覽和刪除
- 添加新日志
- 重點回顧
- 監視器
- 成為監視器
- 向監視器發送命令信息
- 重點回顧
- 源碼、相關資源和勘誤