Redis 設計與實現:資料庫
阿新 • • 發佈:2020-12-25
> 本文的分析都是基於 Redis 6.0 版本原始碼
> redis 6.0 原始碼:https://github.com/redis/redis/tree/6.0
## 伺服器中的資料庫
Redis 伺服器將絕大部分的資訊都儲存在 `server.h/redisServer`。redis 的資料是儲存在 `redisServer` 中的 `redisDb` 結構中。
```c
struct redisServer {
// ...
redisDb *db; // 資料庫列表
// ...
int dbnum; // 資料庫數量
// ...
}
```
- `db` 中每個redisDb結構代表一個數據庫。
- 在初始化伺服器時,程式會根據伺服器狀態的 `dbnum` 屬性來決定應該建立多少個數據庫。
- `dbnum` 屬性的值由伺服器配置的 `database` 選項決定,預設情況下,該選項的值為16,所以Redis伺服器預設會建立16個數據庫。
![](https://img2020.cnblogs.com/blog/756647/202012/756647-20201225110414559-1859726984.png)
## 資料庫鍵空間
Redis 是一個鍵值對資料庫伺服器,伺服器中的每個資料庫都由一個 `server.h/redisDb` 結構表示.
其中,`redisDb` 的 `dict` 字典屬性儲存了資料庫中的所有鍵值對,我們將這個字典稱為鍵空間(key space):
```c
typedef struct redisDb {
dict *dict;
// ...
} redisDb;
```
dict 中的資料跟我們平常操作的鍵值對是一一對應的:
- dict 的 key 就是資料庫中的 key,字串型別
- dict 的 值 就是資料庫中的 值,這個值可以是 `string`、`hash`、`zset`、`set`、`list` 中的任何一種
### 示例
如果我們在資料庫中,執行以下命令:
```shell
redis > SET str_key str_value
OK
redis > RPUSH list_key a b c
(integer) 3
```
新新增的兩個 key 的結構如下圖所示:
![RedisDb 結構示例](https://img2020.cnblogs.com/blog/756647/202012/756647-20201225110309657-146862154.png)
從上面的示例圖可以很清晰地知道 Redis 資料是如何組織的,增刪改查也就是對 dict 的操作而已,此處就不詳細說了。
## Key 的過期時間
### 1. 資料結構
redisDb 中的 `expires` 屬性儲存了所有 `key` 的過期時間,我們姑且就稱它為**過期字典**吧。
- 過期字典中的鍵,是一個指標,指向了真實資料的 `key`,不會浪費空間多儲存一次
- 過期字典中的值,存的是具體的過期時間點,精確到毫秒的時間戳
```c
typedef struct redisDb {
// ...
// 儲存了所有 key 的過期時間
dict *expires;
// ...
} redisDb;
```
命令`TTL`、`PTTL` 都是去查這個過期字典的過期時間,然後減去當前時間,得到的就是剩餘的時間啦。
### 2. 過期 key 的刪除策略
一個 key 過期時間到了之後,是如何進行刪除的呢?Redis 使用了一下兩種策略:惰性刪除、定期刪除
#### 惰性刪除
惰性刪除策略指的是:key 在過期之後,沒有立即刪除,而是在讀寫 key 的時候,才對過期的 key 進行刪除。
程式碼實現在 `db.c/expireIfNeeded` 方法中。所有 key 的讀寫之前,都會先呼叫 `expireIfNeeded` 對 key 進行檢查,如果已過期,則刪除。
#### 定期刪除
定期刪除策略指的是:Redis 每隔一段時間,隨機從資料庫中取出一定量的 key 進行檢查,如果已過期,則進行刪除。
程式碼實現在 `expire.c/activeExpireCycle` 方