redis支援的五種資料類型及其底層實現

來源:互聯網
上載者:User

Redis物件類型簡介

Redis是一種key/value型資料庫,其中,每個key和value都是使用對象表示的。比如,我們執行以下代碼: 其中的key是message,是一個包含了字串"message"的對象。而value是一個包含了"hello redis"的對象。

Redis共有五種對象的類型,分別是:

類型常量 對象的名稱
REDIS_STRING 字串對象
REDIS_LIST 列表對象
REDIS_HASH 雜湊對象
REDIS_SET 集合對象
REDIS_ZSET 有序集合對象

Redis中的一個對象的結構體表示如下:

/*
 * Redis 對象
 */
typedef struct redisObject {

    // 類型
    unsigned type:4;        

    // 不使用(對齊位)
    unsigned notused:2;

    // 編碼方式
    unsigned encoding:4;

    // LRU 時間(相對於 server.lruclock)
    unsigned lru:22;

    // 引用計數
    int refcount;

    // 指向對象的值
    void *ptr;

} robj;

type表示了該對象的物件類型,即上面五個中的一個。但為了提高儲存效率與程式執行效率,每種對象的底層資料結構實現都可能不止一種。encoding就表示了對象底層所使用的編碼。下面先介紹每種底層資料結構的實現,再介紹每種物件類型都用了什麼底層結構並分析他們之間的關係。


Redis對象底層資料結構

底層資料結構共有八種,如下表所示:

編碼常量 編碼所對應的底層資料結構
REDIS_ENCODING_INT long 類型的整數
REDIS_ENCODING_EMBSTR embstr 編碼的簡單動態字串
REDIS_ENCODING_RAW 簡單動態字串
REDIS_ENCODING_HT 字典
REDIS_ENCODING_LINKEDLIST 雙端鏈表
REDIS_ENCODING_ZIPLIST 壓縮列表
REDIS_ENCODING_INTSET 整數集合
REDIS_ENCODING_SKIPLIST 跳躍表和字典


字串對象

字串對象的編碼可以是int、raw或者embstr。

如果一個字串的內容可以轉換為long,那麼該字串就會被轉換成為long類型,對象的ptr就會指向該long,並且物件類型也用int類型表示。

普通的字串有兩種,embstr和raw。embstr應該是Redis 3.0新增的資料結構,在2.8中是沒有的。如果字串對象的長度小於39位元組,就用embstr對象。否則用傳統的raw對象。 embstr的好處有如下幾點: embstr的建立只需分配一次記憶體,而raw為兩次(一次為sds指派至,另一次為objet指派至,embstr省去了第一次)。 相對地,釋放記憶體的次數也由兩次變為一次。 embstr的objet和sds放在一起,更好地利用緩衝帶來的優勢。 需要注意的是,redis並未提供任何修改embstr的方式,即embstr是唯讀形式。對embstr的修改實際上是先轉換為raw再進行修改。


列表對象

列表對象的編碼可以是ziplist或者linkedlist。

ziplist是一種壓縮鏈表,它的好處是更能節省記憶體空間,因為它所儲存的內容都是在連續的記憶體地區當中的。當列表對象元素不大,每個元素也不大的時候,就採用ziplist儲存。但當資料量過大時就ziplist就不是那麼好用了。因為為了保證他儲存內容在記憶體中的連續性,插入的複雜度是O(N),即每次插入都會重新進行realloc。如下圖所示,對象結構中ptr所指向的就是一個ziplist。整個ziplist只需要malloc一次,它們在記憶體中是一塊連續的地區。



linkedlist是一種雙向鏈表。它的結構比較簡單,節點中存放pre和next兩個指標,還有節點相關的資訊。當每增加一個node的時候,就需要重新malloc一塊記憶體。


雜湊對象


雜湊對象的底層實現可以是ziplist或者hashtable。

ziplist中的雜湊對象是按照key1,value1,key2,value2這樣的順序存放來儲存的。當對象數目不多且內容不大時,這種方式效率是很高的。

hashtable的是由dict這個結構來實現的 集合對象

集合對象的編碼可以是intset或者hashtable。

intset是一個整數集合,裡面存的為某種同一類型的整數,支援如下三種長度的整數: [cpp] view plain copy #define INTSET_ENC_INT16 (sizeof(int16_t))   #define INTSET_ENC_INT32 (sizeof(int32_t))   #define INTSET_ENC_INT64 (sizeof(int64_t))   intset是一個有序集合,尋找元素的複雜度為O(logN),但插入時不一定為O(logN),因為有可能涉及到升級操作。比如當集合裡全是int16_t型的整數,這時要插入一個int32_t,那麼為了維持集合中資料類型的一致,那麼所有的資料都會被轉換成int32_t類型,涉及到記憶體的重新分配,這時插入的複雜度就為O(N)了。是intset不支援降級操作。
有序集合對象

有序集合的編碼可能兩種,一種是ziplist,另一種是skiplist與dict的結合。

ziplist作為集合和作為雜湊對象是一樣的,member和score順序存放。按照score從小到大順序排列。它的結構不再複述。

skiplist是一種跳躍表,它實現了有序集合中的快速尋找,在大多數情況下它的速度都可以和平衡樹差不多。但它的實現比較簡單,可以作為平衡樹的替代品。它的結構比較特殊。下面分別是跳躍表skiplist和它內部的節點skiplistNode的結構體:



Redis的索引值可以使用五種資料類型:字串,散列表,列表,集合,有序集合




字串類型

字串是Redis中最基本的資料類型,它能夠儲存任何類型的字串,包含位元據。可以用於儲存郵箱,JSON化的對象,甚至是一張圖片,一個字串允許儲存的最大容量為512MB。字串是其他四種類型的基礎,與其他幾種類型的區別從本質上來說只是組織字串的方式不同而已。 基本命令 字串操作 SET 賦值,用法: SET key value GET 取值,用法: GET key INCR 遞增數字,僅僅對數字類型的鍵有用,相當於Java的i++運算,用法: INCR key INCRBY 增加指定的數字,僅僅對數字類型的鍵有用,相當於Java的i+=3,用法:INCRBY key increment,意思是key自增increment,increment可以為負數,表示減少。 DECR 遞減數字,僅僅對數字類型的鍵有用,相當於Java的i–,用法:DECR key DECRBY 減少指定的數字,僅僅對數字類型的鍵有用,相當於Java的i-=3,用法:DECRBY key decrement,意思是key自減decrement,decrement可以為正數,表示增加。 INCRBYFLOAT 增加指定浮點數,僅僅對數字類型的鍵有用,用法:INCRBYFLOAT key increment APPEND 向尾部追加值,相當於Java中的”hello”.append(“ world”),用法:APPEND key value STRLEN 擷取字串長度,用法:STRLEN key MSET 同時設定多個key的值,用法:MSET key1 value1 [key2 value2 ...] MGET 同時擷取多個key的值,用法:MGET key1 [key2 ...] 位操作 GETBIT 擷取一個索引值的二進位位的指定位置的值(0/1),用法:GETBIT key offset SETBIT 設定一個索引值的二進位位的指定位置的值(0/1),用法:SETBIT key offset value BITCOUNT 擷取一個索引值的一個範圍內的二進位表示的1的個數,用法:BITCOUNT key [start end] BITOP 該命令可以對多個字串類型鍵進行位元運算,並將結果儲存到指定的鍵中,BITOP支援的運算包含:OR,AND,XOR,NOT,用法:BITOP OP desKey key1 key2 BITPOS 擷取指定鍵的第一個位值為0或者1的位置,用法:BITPOS key 0/1 [start, end]

散列類型

散列類型相當於Java中的HashMap,他的值是一個字典,儲存很多key,value對,每對key,value的值個鍵都是字串類型,換句話說,散列類型不能嵌套其他資料類型。一個散列類型鍵最多可以包含2的32次方-1個欄位。 基本命令 HSET 賦值,用法:HSET key field value HMSET 一次賦值多個欄位,用法:HMSET key field1 value1 [field2 values] HGET 取值,用法:HSET key field HMGET 一次取多個欄位的值,用法:HMSET key field1 [field2] HGETALL 一次取所有欄位的值,用法:HGETALL key HEXISTS 判斷欄位是否存在,用法:HEXISTS key field HSETNX 當欄位不存在時賦值,用法:HSETNX key field value HINCRBY 增加數字,僅對數字類型的值有用,用法:HINCRBY key field increment HDEL 刪除欄位,用法:HDEL key field HKEYS 擷取所有欄位名,用法:HKEYS key HVALS 擷取所有欄位值,用法:HVALS key HLEN 擷取欄位數量,用法:HLEN key 清單類型

清單類型(list)用於儲存一個有序的字串列表,常用的操作是向隊列兩端添加元素或者獲得列表的某一片段。列表內部使用的是雙向鏈表(double linked list)實現的,所以向列表兩端添加元素的時間複雜度是O(1),擷取越接近列表兩端的元素的速度越快。但是缺點是使用列表通過索引訪問元素的效率太低(需要從端點開始遍曆元素)。所以列表的使用情境一般如:朋友圈新鮮事,只關心最新的一些內容。藉助清單類型,Redis還可以作為訊息佇列使用。

基本命令 LPUSH 向列表左端添加元素,用法:LPUSH key value RPUSH 向列表右端添加元素,用法:RPUSH key value LPOP 從列表左端彈出元素,用法:LPOP key RPOP 從列表右端彈出元素,用法:RPOP key LLEN 擷取列表中元素個數,用法:LLEN key LRANGE 擷取列表中某一片段的元素,用法:LRANGE key start stop,index從0開始,-1表示最後一個元素 LREM 刪除列表中指定的值,用法:LREM key count value,刪除列表中前count個值為value的元素,當count>0時從左邊開始數,count<0時從右邊開始數,count=0時會刪除所有值為value的元素 LINDEX 擷取指定索引的元素值,用法:LINDEX key index LSET 設定指定索引的元素值,用法:LSET key index value LTRIM 只保留列表指定片段,用法:LTRIM key start stop,包含start和stop LINSERT 像列表中插入元素,用法:LINSERT key BEFORE|AFTER privot value,從左邊開始尋找值為privot的第一個元素,然後根據第二個參數是BEFORE還是AFTER決定在該元素的前面還是後面插入value RPOPLPUSH 將元素從一個列錶轉義到另一個列表,用法:RPOPLPUSH source destination 集合類型

集合在概念在高中課本就學過,集合中每個元素都是不同的,集合中的元素個數最多為2的32次方-1個,集合中的元素師沒有順序的。 基本命令 SADD 添加元素,用法:SADD key value1 [value2 value3 ...] SREM 刪除元素,用法:SREM key value2 [value2 value3 ...] SMEMBERS 獲得集合中所有元素,用法:SMEMBERS key SISMEMBER 判斷元素是否在集合中,用法:SISMEMBER key value SDIFF 對集合做差集運算,用法:SDIFF key1 key2 [key3 ...],先計算key1和key2的差集,然後再用結果與key3做差集 SINTER 對集合做交集運算,用法:SINTER key1 key2 [key3 ...] SUNION 對集合做並集運算,用法:SUNION key1 key2 [key3 ...] SCARD 獲得集合中元素的個數,用法:SCARD key SDIFFSTORE 對集合做差集並將結果儲存,用法:SDIFFSTORE destination key1 key2 [key3 ...] SINTERSTORE 對集合做交集運算並將結果儲存,用法:SINTERSTORE destination key1 key2 [key3 ...] SUNIONSTORE 對集合做並集運算並將結果儲存,用法:SUNIONSTORE destination key1 key2 [key3 ...] SRANDMEMBER 隨機擷取集合中的元素,用法:SRANDMEMBER key [count],當count>0時,會隨機中集合中擷取count個不重複的元素,當count<0時,隨機中集合中擷取|count|和可能重複的元素。 SPOP 從集合中隨機彈出一個元素,用法:SPOP key 有序集合類型

有序集合類型與集合類型的區別就是他是有序的。有序集合是在集合的基礎上為每一個元素關聯一個分數,這就讓有序集合不僅支援插入,刪除,判斷元素是否存在等操作外,還支援擷取分數最高/最低的前N個元素。有序集合中的每個元素是不同的,但是分數卻可以相同。有序集合使用散列表和跳躍表實現,即使讀取位於中間部分的資料也很快,時間複雜度為O(log(N)),有序集合比列表更費記憶體。 基本命令 ZADD 添加元素,用法:ZADD key score1 value1 [score2 value2 score3 value3 ...] ZSCORE 擷取元素的分數,用法:ZSCORE key value ZRANGE 擷取排名在某個範圍的元素,用法:ZRANGE key start stop [WITHSCORE],按照元素從小到大的順序排序,從0開始編號,包含start和stop對應的元素,WITHSCORE選項表示是否返回元素分數 ZREVRANGE 擷取排名在某個範圍的元素,用法:ZREVRANGE key start stop [WITHSCORE],和上一個命令用法一樣,只是這個倒序排序的。 ZRANGEBYSCORE 擷取指定分數範圍內的元素,用法:ZRANGEBYSCORE key min max,包含min和max,(min表示不包含min,(max表示不包含max,+inf表示無窮大 ZINCRBY 增加某個元素的分數,用法:ZINCRBY key increment value ZCARD 擷取集合中元素的個數,用法:ZCARD key ZCOUNT 擷取指定分數範圍內的元素個數,用法:ZCOUNT key min max,min和max的用法和5中的一樣 ZREM 刪除一個或多個元素,用法:ZREM key value1 [value2 ...] ZREMRANGEBYRANK 按照排名範圍刪除元素,用法:ZREMRANGEBYRANK key start stop ZREMRANGEBYSCORE 按照分數範圍刪除元素,用法:ZREMRANGEBYSCORE key min max,min和max的用法和4中的一樣 ZRANK 擷取正序排序的元素的排名,用法:ZRANK key value ZREVRANK 擷取逆序排序的元素的排名,用法:ZREVRANK key value ZINTERSTORE 計算有序集合的交集並儲存結果,用法:ZINTERSTORE destination numbers key1 key2 [key3 key4 ...] WEIGHTS weight1 weight2 [weight3 weight4 ...] AGGREGATE SUM | MIN | MAX,numbers表示參加運算的集合個數,weight表示權重,aggregate表示結果取值 ZUNIONSTORE 計算有序幾個的並集並儲存結果,用法和14一樣,不再贅述。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.