redis的五大數據類型應用場景分析
1、對象的類型與編碼
Redis使用前面說的五大數據類型來表示鍵和值,每次在Redis數據庫中創建一個鍵值對時,至少會創建兩個對象,一個是鍵對象,一個是值對象,而Redis中的每個對象都是由 redisObject 結構來表示:
typedef struct redisObject{ //類型 unsigned type:4; //編碼 unsigned encoding:4; //指向底層數據結構的指針 void *ptr; //引用計數 int refcount; //記錄最後一次被程序訪問的時間 unsigned lru:22; }robj
①、type屬性
對象的type屬性記錄瞭對象的類型,這個類型就是前面講的五大數據類型:
可以通過如下命令來判斷對象類型:
type key
註意:在Redis中,鍵總是一個字符串對象,而值可以是字符串、列表、集合等對象,所以我們通常說的鍵為字符串鍵,表示的是這個鍵對應的值為字符串對象,我們說一個鍵為集合鍵時,表示的是這個鍵對應的值為集合對象。
②、encoding 屬性和 *prt 指針
對象的 prt 指針指向對象底層的數據結構,而數據結構由 encoding 屬性來決定。
而每種類型的對象都至少使用瞭兩種不同的編碼:
可以通過如下命令查看值對象的編碼:
OBJECT ENCODING key
比如 string 類型:
2、字符串對象
字符串是Redis最基本的數據類型,不僅所有key都是字符串類型,其它幾種數據類型構成的元素也是字符串。註意字符串的長度不能超過512M。
①、編碼
字符串對象的編碼可以是int,raw或者embstr。
1、int 編碼:保存的是可以用 long 類型表示的整數值。
2、raw 編碼:保存長度大於44字節的字符串。
3、embstr 編碼:保存長度小於44字節的字符串。
由上可以看出,int 編碼是用來保存整數值,raw編碼是用來保存長字符串,而embstr是用來保存短字符串。其實 embstr 編碼是專門用來保存短字符串的一種優化編碼,raw 和 embstr 的區別:
embstr與raw都使用redisObject和sds保存數據,區別在於,embstr的使用隻分配一次內存空間(因此redisObject和sds是連續的),而raw需要分配兩次內存空間(分別為redisObject和sds分配空間)。因此與raw相比,embstr的好處在於創建時少分配一次空間,刪除時少釋放一次空間,以及對象的所有數據連在一起,尋找方便。而embstr的壞處也很明顯,如果字符串的長度增加需要重新分配內存時,整個redisObject和sds都需要重新分配空間,因此redis中的embstr實現為隻讀。
ps:Redis中對於浮點數類型也是作為字符串保存的,在需要的時候再將其轉換成浮點數類型。
②、編碼的轉換
當 int 編碼保存的值不再是整數,或大小超過瞭long的范圍時,自動轉化為raw。
對於 embstr 編碼,由於 Redis 沒有對其編寫任何的修改程序(embstr 是隻讀的),在對embstr對象進行修改時,都會先轉化為raw再進行修改,因此,隻要是修改embstr對象,修改後的對象一定是raw的,無論是否達到瞭44個字節。
3、列表對象
list 列表,它是簡單的字符串列表,按照插入順序排序,你可以添加一個元素到列表的頭部(左邊)或者尾部(右邊),它的底層實際上是個鏈表結構。
①、編碼
列表對象的編碼可以是 ziplist(壓縮列表) 和 linkedlist(雙端鏈表)。
比如我們執行以下命令,創建一個 key = ‘numbers’,value = ‘1 three 5′ 的三個值的列表。
rpush numbers 1 “three” 5
ziplist 編碼表示如下:
linkedlist表示如下:
②、編碼轉換
當同時滿足下面兩個條件時,使用ziplist(壓縮列表)編碼:
1、列表保存元素個數小於512個
2、每個元素長度小於64字節
不能滿足這兩個條件的時候使用 linkedlist 編碼。
上面兩個條件可以在redis.conf 配置文件中的 list-max-ziplist-value選項和 list-max-ziplist-entries 選項進行配置。
4、哈希對象
哈希對象的鍵是一個字符串類型,值是一個鍵值對集合。
①、編碼
哈希對象的編碼可以是 ziplist 或者 hashtable。
當使用ziplist,也就是壓縮列表作為底層實現時,新增的鍵值對是保存到壓縮列表的表尾。比如執行以下命令:
hset profile name "Tom" hset profile age 25 hset profile career "Programmer"
如果使用ziplist,profile 存儲如下:
當使用 hashtable 編碼時,上面命令存儲如下:
hashtable 編碼的哈希表對象底層使用字典數據結構,哈希對象中的每個鍵值對都使用一個字典鍵值對。
在前面介紹壓縮列表時,我們介紹過壓縮列表是Redis為瞭節省內存而開發的,是由一系列特殊編碼的連續內存塊組成的順序型數據結構,相對於字典數據結構,壓縮列表用於元素個數少、元素長度小的場景。其優勢在於集中存儲,節省空間。
②、編碼轉換
和上面列表對象使用 ziplist 編碼一樣,當同時滿足下面兩個條件時,使用ziplist(壓縮列表)編碼:
1、列表保存元素個數小於512個
2、每個元素長度小於64字節
不能滿足這兩個條件的時候使用 hashtable 編碼。第一個條件可以通過配置文件中的 set-max-intset-entries 進行修改。
5、集合對象
集合對象 set 是 string 類型(整數也會轉換成string類型進行存儲)的無序集合。註意集合和列表的區別:集合中的元素是無序的,因此不能通過索引來操作元素;集合中的元素不能有重復。
①、編碼
集合對象的編碼可以是 intset 或者 hashtable。
intset 編碼的集合對象使用整數集合作為底層實現,集合對象包含的所有元素都被保存在整數集合中。
hashtable 編碼的集合對象使用 字典作為底層實現,字典的每個鍵都是一個字符串對象,這裡的每個字符串對象就是一個集合中的元素,而字典的值則全部設置為 null。這裡可以類比Java集合中HashSet 集合的實現,HashSet 集合是由 HashMap 來實現的,集合中的元素就是 HashMap 的key,而 HashMap 的值都設為 null。
SADD numbers 1 3 5
SADD Dfruits “apple” “banana” “cherry”
②、編碼轉換
當集合同時滿足以下兩個條件時,使用 intset 編碼:
1、集合對象中所有元素都是整數
2、集合對象所有元素數量不超過512
不能滿足這兩個條件的就使用 hashtable 編碼。第二個條件可以通過配置文件的 set-max-intset-entries 進行配置。
6、有序集合對象
和上面的集合對象相比,有序集合對象是有序的。與列表使用索引下標作為排序依據不同,有序集合為每個元素設置一個分數(score)作為排序依據。
①、編碼
有序集合的編碼可以是 ziplist 或者 skiplist。
ziplist 編碼的有序集合對象使用壓縮列表作為底層實現,每個集合元素使用兩個緊挨在一起的壓縮列表節點來保存,第一個節點保存元素的成員,第二個節點保存元素的分值。並且壓縮列表內的集合元素按分值從小到大的順序進行排列,小的放置在靠近表頭的位置,大的放置在靠近表尾的位置。
ZADD price 8.5 apple 5.0 banana 6.0 cherry
skiplist 編碼的有序集合對象使用 zet 結構作為底層實現,一個 zset 結構同時包含一個字典和一個跳躍表:
typedef struct zset{ //跳躍表 zskiplist *zsl; //字典 dict *dice; } zset;
字典的鍵保存元素的值,字典的值則保存元素的分值;跳躍表節點的 object 屬性保存元素的成員,跳躍表節點的 score 屬性保存元素的分值。
這兩種數據結構會通過指針來共享相同元素的成員和分值,所以不會產生重復成員和分值,造成內存的浪費。
說明:其實有序集合單獨使用字典或跳躍表其中一種數據結構都可以實現,但是這裡使用兩種數據結構組合起來,原因是假如我們單獨使用 字典,雖然能以 O(1) 的時間復雜度查找成員的分值,但是因為字典是以無序的方式來保存集合元素,所以每次進行范圍操作的時候都要進行排序;假如我們單獨使用跳躍表來實現,雖然能執行范圍操作,但是查找操作有 O(1)的復雜度變為瞭O(logN)。因此Redis使用瞭兩種數據結構來共同實現有序集合。
②、編碼轉換
當有序集合對象同時滿足以下兩個條件時,對象使用 ziplist 編碼:
1、保存的元素數量小於128;
2、保存的所有元素長度都小於64字節。
不能滿足上面兩個條件的使用 skiplist 編碼。以上兩個條件也可以通過Redis配置文件zset-max-ziplist-entries 選項和 zset-max-ziplist-value 進行修改。
7、五大數據類型的應用場景
對於string 數據類型,因為string 類型是二進制安全的,可以用來存放圖片,視頻等內容,另外由於Redis的高性能讀寫功能,而string類型的value也可以是數字,可以用作計數器(INCR,DECR),比如分佈式環境中統計系統的在線人數,秒殺等。
對於 hash 數據類型,value 存放的是鍵值對,比如可以做單點登錄存放用戶信息。
對於 list 數據類型,可以實現簡單的消息隊列,另外可以利用lrange命令,做基於redis的分頁功能
對於 set 數據類型,由於底層是字典實現的,查找元素特別快,另外set 數據類型不允許重復,利用這兩個特性我們可以進行全局去重,比如在用戶註冊模塊,判斷用戶名是否註冊;另外就是利用交集、並集、差集等操作,可以計算共同喜好,全部的喜好,自己獨有的喜好等功能。
對於 zset 數據類型,有序的集合,可以做范圍查找,排行榜應用,取 TOP N 操作等。
到此這篇關於redis的五大數據類型實現原理的文章就介紹到這瞭,更多相關redis數據類型內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!