Redis是一個開源的Key-Value存儲引擎,它支持string、hash、list、set和sorted set等多種值類型。由於其卓越的性能表現、豐富的數據類型及穩定性,廣泛用於各種需要k/v存儲的場景。甚至在一些分布式緩存系統中,也用它作為底層存儲引擎。本文對redis最常用的數據類型進行剖析,從而讓使用者在各自場景下選擇合適的數據類型,從而發揮其最好的優勢。
1、String
String是最常用的一種數據類型,普通的k/v存儲都可以歸為此類,內部由sds.h定義。在類型的設計上,除了包含字符數組buf,還會額外存儲實際字符串的長度以及buf的剩余空間,從而提供更靈活的管理方式。特別需要提到的是,redis自己管理內存,從而自身可以掌握更多關於內存的信息,提高內存分配的性能以及作為其它功能的依據。此外,該結構還提供了一些字符串相關的操作函數,功能豐富,實現透明,使用方便。
常用命令:
命令 |
時間復雜度 |
描述 |
返回值 |
APPEND |
O(1) |
如果該Key已經存在,APPEND命令將參數Value的數據追加到已存在Value的末尾。如果該Key不存在,APPEND命令將會創建一個新的Key/Value。 |
追加后Value的長度 |
INCR |
O(1) |
將指定Key的Value原子性的遞增1。如果該Key不存在,其初始值為0,在incr之后其值為1。如果Value的值不能轉換為整型值,該操作將執行失敗並返回相應的錯誤信息。 |
遞增后的Value值 |
INCRBY |
O(1) |
將指定Key的Value原子性的增加increment。如果該Key不存在,其初始值為0,在incrby之后其值為increment。如果Value的值不能轉換為整型值,該操作將執行失敗並返回相應的錯誤信息。 |
增加后的Value值 |
DECR |
O(1) |
將指定Key的Value原子性的遞減1。如果該Key不存在,其初始值為0,在decr之后其值為-1。如果Value的值不能轉換為整型值,如Hello,該操作將執行失敗並返回相應的錯誤信息。 |
遞減后的Value值。 |
DECRBY |
O(1) |
將指定Key的Value原子性的減少decrement。如果該Key不存在,其初始值為0,在decrby之后其值為-decrement。如果Value的值不能轉換為整型值,如Hello,該操作將執行失敗並返回相應的錯誤信息。 |
減少后的Value值。 |
GET |
O(1) |
獲取指定Key的Value。如果與該Key關聯的Value不是string類型,Redis將返回錯誤信息。 |
與該Key相關的Value,如果該Key不存在,返回nil。 |
SET |
O(1) |
設定該Key持有指定的字符串Value,如果該Key已經存在,則覆蓋其原有值。 |
返回"OK" |
2、Hash
Hash是一個string類型的field和value的映射表,由dict.h定義。一個key可對應多個field,一個field對應一個value。將一個對象存儲為hash類型,較於每個字段都存儲成string類型更能節省內存,並且可以更方便的存取整個對象。hash內部存儲的value為一個hashMap,並提供了直接存取這個Map成員的接口。
舉個例子:我們要存儲一個用戶詳細信息,就對每個用戶設置一個userID作為key,而存儲的value包含了包括姓名name,年齡age,生日birth,住址addr等信息。如果用普通的k/v結構存儲,一般就是userID與value中的每一項組合作為key,而對應的內容作為value。如通過命令
mset user1_name ”張三” user1_age 18 user1_addr “杭州”
這種模式,在數據量大的時候,存在明顯的內存浪費。而redis提供的hash結構就很好的解決了這個問題。對於上面的例子,通過執行命令hmset user1 name ”張三” age 18 addr “杭州”
user1作為key,而name、age、birth等作為屬性。如此,便通過key+field的方式,避免了數據的重復存儲。
提到Hash結構,不得不說的是key的沖突解決辦法和哈希表的擴容方案。在Redis中,在沖突發生時,采取的是鏈式沖突解決辦法。另外,redis采用了雙哈希表結構(ht[2])。簡單來說,就是初始k/v保存在ht[0]中,當沖突嚴重時,將ht[1]中桶的大小設置為ht[0]的兩倍,並逐步將ht[0]中的元素遷移到ht[1]。等到所有元素都遷移完成后,再將ht[0]與ht[1]交換地址。
常用命令:
命令 |
時間復雜度 |
描述 |
返回值 |
HSET |
O(1) |
為指定的Key設定Field/Value對,如果Key不存在,該命令將創建新Key以參數中的Field/Value對,如果參數中的Field在該Key中已經存在,則用新值覆蓋其原有值。 |
1表示新的Field被設置了新值,0表示Field已經存在,用新值覆蓋原有值。 |
HGET |
O(1) |
返回指定Key中指定Field的關聯值。 |
返回參數中Field的關聯值,如果參數中的Key或Field不存,返回nil。 |
HEXISTS |
O(1) |
判斷指定Key中的指定Field是否存在。 |
1表示存在,0表示參數中的Field或Key不存在。 |
3、List
List實現為一個雙向鏈表,支持了反向的插入、查找和遍歷,由adlist.h定義。我們可以通過push,pop操作從鏈表的頭部或者尾部添加刪除元素。在需要隊列這樣的數據結構時,List能提供非常豐富的接口,廣泛用於緩沖隊列,消息隊列等應用場景。學過數據結構的朋友,對此肯定不會陌生。
常用命令:
命令 |
時間復雜度 |
描述 |
返回值 |
LPUSH |
O(1) |
在指定Key所關聯的List Value的頭部插入參數中給出的所有Values。如果該Key不存在,該命令將在插入之前創建一個與該Key關聯的空鏈表,之后再將數據從鏈表的頭部插入。如果該鍵的Value不是鏈表類型,該命令將返回相關的錯誤信息。 |
插入后鏈表中元素的數量。 |
RPUSH |
O(1) |
在指定Key所關聯的List Value的尾部插入參數中給出的所有Values。如果該Key不存在,該命令將在插入之前創建一個與該Key關聯的空鏈表,之后再將數據從鏈表的尾部插入。如果該鍵的Value不是鏈表類型,該命令將返回相關的錯誤信息。 |
插入后鏈表中元素的數量。 |
LPOP |
O(1) |
返回並彈出指定Key關聯的鏈表中的第一個元素,即頭部元素。如果該Key不存,返回nil。 |
鏈表頭部的元素。 |
RPOP |
O(1) |
返回並彈出指定Key關聯的鏈表中的最后一個元素,即尾部元素。如果該Key不存,返回nil。 |
鏈表尾部的元素。 |
LLEN |
O(1) |
返回指定Key關聯的鏈表中元素的數量,如果該Key不存在,則返回0。如果與該Key關聯的Value的類型不是鏈表,則返回相關的錯誤信息。 |
鏈表中元素的數量。 |
4、Set
Set集合類型提供的是一個列表的功能,而且是可以自動去重的。它的內部實現其實是一個value值為null的HashMap,也正是因此來使得數據去重以及判斷某個成員是否在集合內等其它一些重要接口變的簡單。當某些應用場景需要存儲一個列表,且不想要重復的數據時,就可以選擇set這個結構來處理。
常用命令:
命令 |
時間復雜度 |
描述 |
返回值 |
SADD |
O(N) |
時間復雜度中的N表示操作的成員數量。如果在插入的過程用,參數中有的成員在Set中已經存在,該成員將被忽略,而其它成員仍將會被正常插入。如果執行該命令之前,該Key並不存在,該命令將會創建一個新的Set,此后再將參數中的成員陸續插入。如果該Key的Value不是Set類型,該命令將返回相關的錯誤信息。 |
本次操作實際插入的成員數量。 |
SREM |
O(N) |
時間復雜度中的N表示被刪除的成員數量。從與Key關聯的Set中刪除參數中指定的成員,不存在的參數成員將被忽略,如果該Key並不存在,將視為空Set處理。 |
從Set中實際移除的成員數量,如果沒有則返回0。 |
SCARD |
O(1) |
獲取Set中成員的數量。 |
返回Set中成員的數量,如果該Key並不存在,返回0。 |
SISMEMBER |
O(1) |
判斷參數中指定成員是否已經存在於與Key相關聯的Set集合中。 |
1表示已經存在,0表示不存在,或該Key本身並不存在。 |
5、Sort Set
Sort Set的功能與Set非常相似,只不過它是可以通過用戶提供一個優先級參數來實現自動排序的,而Set結構不會做自動排序。Sort set內部使用HashMap和SkipList來實現數據的有序存儲,保證查詢的效率以及元素有序性。在某些應用場景,比如需要為某個班級的學生根據成績來排序,就可以將優先級參數設置為成績分數,這樣在插入到這個結構時,就可以實現自動的排序。
命令 |
時間復雜度 |
描述 |
返回值 |
APPEND |
O(1) |
如果該Key已經存在,APPEND命令將參數Value的數據追加到已存在Value的末尾。如果該Key不存在,APPEND命令將會創建一個新的Key/Value。 |
追加后Value的長度 |
INCR |
O(1) |
將指定Key的Value原子性的遞增1。如果該Key不存在,其初始值為0,在incr之后其值為1。如果Value的值不能轉換為整型值,該操作將執行失敗並返回相應的錯誤信息。 |
遞增后的Value值 |
INCRBY |
O(1) |
將指定Key的Value原子性的增加increment。如果該Key不存在,其初始值為0,在incrby之后其值為increment。如果Value的值不能轉換為整型值,該操作將執行失敗並返回相應的錯誤信息。 |
增加后的Value值 |
DECR |
O(1) |
將指定Key的Value原子性的遞減1。如果該Key不存在,其初始值為0,在decr之后其值為-1。如果Value的值不能轉換為整型值,如Hello,該操作將執行失敗並返回相應的錯誤信息。 |
遞減后的Value值。 |
DECRBY |
O(1) |
將指定Key的Value原子性的減少decrement。如果該Key不存在,其初始值為0,在decrby之后其值為-decrement。如果Value的值不能轉換為整型值,如Hello,該操作將執行失敗並返回相應的錯誤信息。 |
減少后的Value值。 |
GET |
O(1) |
獲取指定Key的Value。如果與該Key關聯的Value不是string類型,Redis將返回錯誤信息。 |
與該Key相關的Value,如果該Key不存在,返回nil。 |
SET |
O(1) |
設定該Key持有指定的字符串Value,如果該Key已經存在,則覆蓋其原有值。 |
返回"OK"
|