Redis 是一個(gè)鍵值對(duì)(key-value pair)數(shù)據(jù)庫(kù)服務(wù)器, 服務(wù)器中的每個(gè)數(shù)據(jù)庫(kù)都由一個(gè) redis.h/redisDb
結(jié)構(gòu)表示, 其中, redisDb
結(jié)構(gòu)的dict
字典保存了數(shù)據(jù)庫(kù)中的所有鍵值對(duì), 我們將這個(gè)字典稱(chēng)為鍵空間(key space):
typedef struct redisDb {
// ...
// 數(shù)據(jù)庫(kù)鍵空間,保存著數(shù)據(jù)庫(kù)中的所有鍵值對(duì)
dict *dict;
// ...
} redisDb;
鍵空間和用戶(hù)所見(jiàn)的數(shù)據(jù)庫(kù)是直接對(duì)應(yīng)的:
舉個(gè)例子, 如果我們?cè)诳瞻椎臄?shù)據(jù)庫(kù)中執(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ù)庫(kù)的鍵空間將會(huì)是圖 IMAGE_DB_EXAMPLE 所展示的樣子:
alphabet
是一個(gè)列表鍵, 鍵的名字是一個(gè)包含字符串 "alphabet"
的字符串對(duì)象, 鍵的值則是一個(gè)包含三個(gè)元素的列表對(duì)象。book
是一個(gè)哈希表鍵, 鍵的名字是一個(gè)包含字符串 "book"
的字符串對(duì)象, 鍵的值則是一個(gè)包含三個(gè)鍵值對(duì)的哈希表對(duì)象。message
是一個(gè)字符串鍵, 鍵的名字是一個(gè)包含字符串 "message"
的字符串對(duì)象, 鍵的值則是一個(gè)包含字符串 "hello world"
的字符串對(duì)象。因?yàn)閿?shù)據(jù)庫(kù)的鍵空間是一個(gè)字典, 所以所有針對(duì)數(shù)據(jù)庫(kù)的操作 —— 比如添加一個(gè)鍵值對(duì)到數(shù)據(jù)庫(kù), 或者從數(shù)據(jù)庫(kù)中刪除一個(gè)鍵值對(duì), 又或者在數(shù)據(jù)庫(kù)中獲取某個(gè)鍵值對(duì), 等等, 實(shí)際上都是通過(guò)對(duì)鍵空間字典進(jìn)行操作來(lái)實(shí)現(xiàn)的, 以下幾個(gè)小節(jié)將分別介紹數(shù)據(jù)庫(kù)的添加、刪除、更新、取值等操作的實(shí)現(xiàn)原理。
添加一個(gè)新鍵值對(duì)到數(shù)據(jù)庫(kù), 實(shí)際上就是將一個(gè)新鍵值對(duì)添加到鍵空間字典里面, 其中鍵為字符串對(duì)象, 而值則為任意一種類(lèi)型的 Redis 對(duì)象。
舉個(gè)例子, 如果鍵空間當(dāng)前的狀態(tài)如圖 IMAGE_DB_EXAMPLE 所示, 那么在執(zhí)行以下命令之后:
redis> SET date "2013.12.1"
OK
鍵空間將添加一個(gè)新的鍵值對(duì), 這個(gè)新鍵值對(duì)的鍵是一個(gè)包含字符串 "date"
的字符串對(duì)象, 而鍵值對(duì)的值則是一個(gè)包含字符串 "2013.12.1"
的字符串對(duì)象, 如圖 IMAGE_DB_AFTER_ADD_NEW_KEY 所示。
刪除數(shù)據(jù)庫(kù)中的一個(gè)鍵, 實(shí)際上就是在鍵空間里面刪除鍵所對(duì)應(yīng)的鍵值對(duì)對(duì)象。
舉個(gè)例子, 如果鍵空間當(dāng)前的狀態(tài)如圖 IMAGE_DB_EXAMPLE 所示, 那么在執(zhí)行以下命令之后:
redis> DEL book
(integer) 1
鍵 book
以及它的值將從鍵空間中被刪除, 如圖 IMAGE_DB_AFTER_DEL 所示。
對(duì)一個(gè)數(shù)據(jù)庫(kù)鍵進(jìn)行更新, 實(shí)際上就是對(duì)鍵空間里面鍵所對(duì)應(yīng)的值對(duì)象進(jìn)行更新, 根據(jù)值對(duì)象的類(lèi)型不同, 更新的具體方法也會(huì)有所不同。
舉個(gè)例子, 如果鍵空間當(dāng)前的狀態(tài)如圖 IMAGE_DB_EXAMPLE 所示, 那么在執(zhí)行以下命令之后:
redis> SET message "blah blah"
OK
鍵 message
的值對(duì)象將從之前包含 "hello world"
字符串更新為包含 "blah blah"
字符串, 如圖 IMAGE_DB_UPDATE_CAUSE_SET 所示。
再舉個(gè)例子, 如果我們繼續(xù)執(zhí)行以下命令:
redis> HSET book page 320
(integer) 1
那么鍵空間中 book
鍵的值對(duì)象(一個(gè)哈希對(duì)象)將被更新, 新的鍵值對(duì) page
和 320
會(huì)被添加到值對(duì)象里面, 如圖 IMAGE_UPDATE_BY_HSET 所示。
對(duì)一個(gè)數(shù)據(jù)庫(kù)鍵進(jìn)行取值, 實(shí)際上就是在鍵空間中取出鍵所對(duì)應(yīng)的值對(duì)象, 根據(jù)值對(duì)象的類(lèi)型不同, 具體的取值方法也會(huì)有所不同。
舉個(gè)例子, 如果鍵空間當(dāng)前的狀態(tài)如圖 IMAGE_DB_EXAMPLE 所示, 那么當(dāng)執(zhí)行以下命令時(shí):
redis> GET message
"hello world"
GET 命令將首先在鍵空間中查找鍵 message
, 找到鍵之后接著取得該鍵所對(duì)應(yīng)的字符串對(duì)象值, 之后再返回值對(duì)象所包含的字符串 "helloworld"
, 取值過(guò)程如圖 IMAGE_FETCH_VALUE_VIA_GET 所示。
再舉一個(gè)例子, 當(dāng)執(zhí)行以下命令時(shí):
redis> LRANGE alphabet 0 -1
1) "a"
2) "b"
3) "c"
LRANGE 命令將首先在鍵空間中查找鍵 alphabet
, 找到鍵之后接著取得該鍵所對(duì)應(yīng)的列表對(duì)象值, 之后再返回列表對(duì)象中包含的三個(gè)字符串對(duì)象的值, 取值過(guò)程如圖 IMAGE_FETCH_VALUE_VIA_LRANGE 所示。
除了上面列出的添加、刪除、更新、取值操作之外, 還有很多針對(duì)數(shù)據(jù)庫(kù)本身的 Redis 命令, 也是通過(guò)對(duì)鍵空間進(jìn)行處理來(lái)完成的。
比如說(shuō), 用于清空整個(gè)數(shù)據(jù)庫(kù)的 FLUSHDB 命令, 就是通過(guò)刪除鍵空間中的所有鍵值對(duì)來(lái)實(shí)現(xiàn)的。
又比如說(shuō), 用于隨機(jī)返回?cái)?shù)據(jù)庫(kù)中某個(gè)鍵的 RANDOMKEY 命令, 就是通過(guò)在鍵空間中隨機(jī)返回一個(gè)鍵來(lái)實(shí)現(xiàn)的。
另外, 用于返回?cái)?shù)據(jù)庫(kù)鍵數(shù)量的 DBSIZE 命令, 就是通過(guò)返回鍵空間中包含鍵值對(duì)的數(shù)量來(lái)實(shí)現(xiàn)的。
類(lèi)似的命令還有 EXISTS 、 RENAME 、 KEYS , 等等, 這些命令都是通過(guò)對(duì)鍵空間進(jìn)行操作來(lái)實(shí)現(xiàn)的。
當(dāng)使用 Redis 命令對(duì)數(shù)據(jù)庫(kù)進(jìn)行讀寫(xiě)時(shí), 服務(wù)器不僅會(huì)對(duì)鍵空間執(zhí)行指定的讀寫(xiě)操作, 還會(huì)執(zhí)行一些額外的維護(hù)操作, 其中包括:
keyspace_hits
屬性和 keyspace_misses
屬性中查看。key
的閑置時(shí)間。
更多建議: