Redis 數(shù)據(jù)庫鍵空間

2018-08-02 14:50 更新

Redis 是一個鍵值對(key-value pair)數(shù)據(jù)庫服務器, 服務器中的每個數(shù)據(jù)庫都由一個 redis.h/redisDb 結構表示, 其中, redisDb 結構的dict 字典保存了數(shù)據(jù)庫中的所有鍵值對, 我們將這個字典稱為鍵空間(key space):

typedef struct redisDb {

    // ...

    // 數(shù)據(jù)庫鍵空間,保存著數(shù)據(jù)庫中的所有鍵值對
    dict *dict;

    // ...

} redisDb;

鍵空間和用戶所見的數(shù)據(jù)庫是直接對應的:

  • 鍵空間的鍵也就是數(shù)據(jù)庫的鍵, 每個鍵都是一個字符串對象。
  • 鍵空間的值也就是數(shù)據(jù)庫的值, 每個值可以是字符串對象、列表對象、哈希表對象、集合對象和有序集合對象在內的任意一種 Redis 對象。

舉個例子, 如果我們在空白的數(shù)據(jù)庫中執(zhí)行以下命令:

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

那么在這些命令執(zhí)行之后, 數(shù)據(jù)庫的鍵空間將會是圖 IMAGE_DB_EXAMPLE 所展示的樣子:

  • alphabet 是一個列表鍵, 鍵的名字是一個包含字符串 "alphabet" 的字符串對象, 鍵的值則是一個包含三個元素的列表對象。
  • book 是一個哈希表鍵, 鍵的名字是一個包含字符串 "book" 的字符串對象, 鍵的值則是一個包含三個鍵值對的哈希表對象。
  • message 是一個字符串鍵, 鍵的名字是一個包含字符串 "message" 的字符串對象, 鍵的值則是一個包含字符串 "hello world" 的字符串對象。

因為數(shù)據(jù)庫的鍵空間是一個字典, 所以所有針對數(shù)據(jù)庫的操作 —— 比如添加一個鍵值對到數(shù)據(jù)庫, 或者從數(shù)據(jù)庫中刪除一個鍵值對, 又或者在數(shù)據(jù)庫中獲取某個鍵值對, 等等, 實際上都是通過對鍵空間字典進行操作來實現(xiàn)的, 以下幾個小節(jié)將分別介紹數(shù)據(jù)庫的添加、刪除、更新、取值等操作的實現(xiàn)原理。

添加新鍵

添加一個新鍵值對到數(shù)據(jù)庫, 實際上就是將一個新鍵值對添加到鍵空間字典里面, 其中鍵為字符串對象, 而值則為任意一種類型的 Redis 對象。

舉個例子, 如果鍵空間當前的狀態(tài)如圖 IMAGE_DB_EXAMPLE 所示, 那么在執(zhí)行以下命令之后:

redis> SET date "2013.12.1"
OK

鍵空間將添加一個新的鍵值對, 這個新鍵值對的鍵是一個包含字符串 "date" 的字符串對象, 而鍵值對的值則是一個包含字符串 "2013.12.1"的字符串對象, 如圖 IMAGE_DB_AFTER_ADD_NEW_KEY 所示。

刪除鍵

刪除數(shù)據(jù)庫中的一個鍵, 實際上就是在鍵空間里面刪除鍵所對應的鍵值對對象。

舉個例子, 如果鍵空間當前的狀態(tài)如圖 IMAGE_DB_EXAMPLE 所示, 那么在執(zhí)行以下命令之后:

redis> DEL book
(integer) 1

鍵 book 以及它的值將從鍵空間中被刪除, 如圖 IMAGE_DB_AFTER_DEL 所示。

更新鍵

對一個數(shù)據(jù)庫鍵進行更新, 實際上就是對鍵空間里面鍵所對應的值對象進行更新, 根據(jù)值對象的類型不同, 更新的具體方法也會有所不同。

舉個例子, 如果鍵空間當前的狀態(tài)如圖 IMAGE_DB_EXAMPLE 所示, 那么在執(zhí)行以下命令之后:

redis> SET message "blah blah"
OK

鍵 message 的值對象將從之前包含 "hello world" 字符串更新為包含 "blah blah" 字符串, 如圖 IMAGE_DB_UPDATE_CAUSE_SET 所示。

再舉個例子, 如果我們繼續(xù)執(zhí)行以下命令:

redis> HSET book page 320
(integer) 1

那么鍵空間中 book 鍵的值對象(一個哈希對象)將被更新, 新的鍵值對 page 和 320 會被添加到值對象里面, 如圖 IMAGE_UPDATE_BY_HSET 所示。

對鍵取值

對一個數(shù)據(jù)庫鍵進行取值, 實際上就是在鍵空間中取出鍵所對應的值對象, 根據(jù)值對象的類型不同, 具體的取值方法也會有所不同。

舉個例子, 如果鍵空間當前的狀態(tài)如圖 IMAGE_DB_EXAMPLE 所示, 那么當執(zhí)行以下命令時:

redis> GET message
"hello world"

GET 命令將首先在鍵空間中查找鍵 message , 找到鍵之后接著取得該鍵所對應的字符串對象值, 之后再返回值對象所包含的字符串 "helloworld" , 取值過程如圖 IMAGE_FETCH_VALUE_VIA_GET 所示。

再舉一個例子, 當執(zhí)行以下命令時:

redis> LRANGE alphabet 0 -1
1) "a"
2) "b"
3) "c"

LRANGE 命令將首先在鍵空間中查找鍵 alphabet , 找到鍵之后接著取得該鍵所對應的列表對象值, 之后再返回列表對象中包含的三個字符串對象的值, 取值過程如圖 IMAGE_FETCH_VALUE_VIA_LRANGE 所示。

其他鍵空間操作

除了上面列出的添加、刪除、更新、取值操作之外, 還有很多針對數(shù)據(jù)庫本身的 Redis 命令, 也是通過對鍵空間進行處理來完成的。

比如說, 用于清空整個數(shù)據(jù)庫的 FLUSHDB 命令, 就是通過刪除鍵空間中的所有鍵值對來實現(xiàn)的。

又比如說, 用于隨機返回數(shù)據(jù)庫中某個鍵的 RANDOMKEY 命令, 就是通過在鍵空間中隨機返回一個鍵來實現(xiàn)的。

另外, 用于返回數(shù)據(jù)庫鍵數(shù)量的 DBSIZE 命令, 就是通過返回鍵空間中包含鍵值對的數(shù)量來實現(xiàn)的。

類似的命令還有 EXISTS 、 RENAME 、 KEYS , 等等, 這些命令都是通過對鍵空間進行操作來實現(xiàn)的。

讀寫鍵空間時的維護操作

當使用 Redis 命令對數(shù)據(jù)庫進行讀寫時, 服務器不僅會對鍵空間執(zhí)行指定的讀寫操作, 還會執(zhí)行一些額外的維護操作, 其中包括:

  • 在讀取一個鍵之后(讀操作和寫操作都要對鍵進行讀?。?, 服務器會根據(jù)鍵是否存在, 以此來更新服務器的鍵空間命中(hit)次數(shù)或鍵空間不命中(miss)次數(shù), 這兩個值可以在 INFO stats 命令的 keyspace_hits 屬性和 keyspace_misses 屬性中查看。
  • 在讀取一個鍵之后, 服務器會更新鍵的 LRU (最后一次使用)時間, 這個值可以用于計算鍵的閑置時間, 使用命令 OBJECT idletime  命令可以查看鍵 key 的閑置時間。
  • 如果服務器在讀取一個鍵時, 發(fā)現(xiàn)該鍵已經(jīng)過期, 那么服務器會先刪除這個過期鍵, 然后才執(zhí)行余下的其他操作, 本章稍后對過期鍵的討論會詳細說明這一點。
  • 如果有客戶端使用 WATCH 命令監(jiān)視了某個鍵, 那么服務器在對被監(jiān)視的鍵進行修改之后, 會將這個鍵標記為臟(dirty), 從而讓事務程序注意到這個鍵已經(jīng)被修改過, 《事務》一章會詳細說明這一點。
  • 服務器每次修改一個鍵之后, 都會對臟(dirty)鍵計數(shù)器的值增一, 這個計數(shù)器會觸發(fā)服務器的持久化以及復制操作執(zhí)行, 《RDB 持久化》、《AOF 持久化》和《復制》這三章都會說到這一點。
  • 如果服務器開啟了數(shù)據(jù)庫通知功能, 那么在對鍵進行修改之后, 服務器將按配置發(fā)送相應的數(shù)據(jù)庫通知, 本章稍后討論數(shù)據(jù)庫通知功能的實現(xiàn)時會詳細說明這一點。


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號