Redis之?dāng)?shù)據(jù)結(jié)構(gòu)

Redis支持五中數(shù)據(jù)類型:

  • String(字符串)
  • Hash(哈希)
  • List(列表)
  • Set(集合)
  • zset(sorted set:有序集合)

Redis定義了豐富的原語命令,可以直接與Redis服務(wù)器交互溪猿。實(shí)際應(yīng)用中钩杰,我們不太會(huì)直接使用這些原語命令,Redis提供了Java再愈,C/C++榜苫,C#,PHP翎冲,JavaScript垂睬,Perl,Object-C抗悍,Python驹饺,Ruby,Erlang等客戶端缴渊,大多情況下我們是通過各式各樣的客戶端來操作Redis赏壹。但是,任何語言的客戶端實(shí)際上都是對(duì)Redis原語命令的封裝衔沼,了解原語命令有助于理解客戶端的設(shè)計(jì)原理蝌借,知其然,知其所以然指蚁。

1菩佑、字符串

String是Redis最基本的數(shù)據(jù)類型,結(jié)構(gòu)為一個(gè)key對(duì)應(yīng)一個(gè)value凝化。

String類型是二進(jìn)制安全的稍坯,意味著可以包含任何數(shù)據(jù),比如jpg圖片或者序列化的對(duì)象搓劫。

String類型的最大能存儲(chǔ)512M瞧哟。

不像Linux有那么多充滿想象力的命令混巧,還喜歡帶一對(duì)莫名其妙的參數(shù)。Redis的原語命令很簡(jiǎn)單勤揩,而且有規(guī)律可循咧党,一句話概括,就是干凈利索脆雄可。

比如我們想設(shè)置往Redis中存放一個(gè)用戶名凿傅,用String類型存儲(chǔ):

127.0.0.1:6379> SET name chenlongfei

OK

“OK”是Redis返回的響應(yīng),代表設(shè)置成功数苫。

取出這個(gè)name的值:

127.0.0.1:6379> GET name

"chenlongfei"

想修改name的值為“clf”聪舒,重新SET一遍,覆蓋掉原來的值:

127.0.0.1:6379> SET name clf

OK

127.0.0.1:6379> GET name

"clf"

想刪除該條數(shù)據(jù):

127.0.0.1:6379> DEL name

(integer) 1 --該數(shù)字代表影響的記錄總數(shù)

127.0.0.1:6379> GET name

(nil) --nil代表為空虐急,不存在該對(duì)象

增刪改查命令一分鐘學(xué)會(huì)箱残,想忘記都難,媽媽再也不用擔(dān)心我的學(xué)習(xí)止吁。

命令格式 說明
SET key value 設(shè)置指定 key 的值
GET key 獲取指定 key 的值
SETNX key value Set if Not Exist)只有在 key 不存在時(shí)設(shè)置 key 的值
SETRANGE key offset value 用 value 參數(shù)覆寫給定 key 所儲(chǔ)存的字符串值被辑,從偏移量 offset 開始
GETRANGE key start end 返回 key 中字符串值的子字符
GETSET key value 將給定 key 的值設(shè)為 value ,并返回 key 的舊值
MSET key value [key value ...] Multi Set)同時(shí)設(shè)置一個(gè)或多個(gè) key-value 對(duì)
MGET key1 [key2..] 獲取所有(一個(gè)或多個(gè))給定 key 的值
APPEND key value 如果 key 已經(jīng)存在并且是一個(gè)字符串敬惦, APPEND 命令將 value 追加到 key 原來的值的末尾
SETEX key seconds value Set Expire)將值 value 關(guān)聯(lián)到 key 盼理,并將 key 的過期時(shí)間設(shè)為 seconds (以秒為單位)
PSETEX key milliseconds value Precise Set Expire)這個(gè)命令和 SETEX 命令相似,但它以毫秒為單位設(shè)置 key 的生存時(shí)間俄删,而不是像 SETEX 命令那樣宏怔,以秒為單位
STRLEN key 返回 key 所儲(chǔ)存的字符串值的長(zhǎng)度
INCR key 將 key 中儲(chǔ)存的數(shù)字值增一,前提是value是一個(gè)數(shù)字
INCRBY key increment 將 key 所儲(chǔ)存的值加上給定的增量值畴椰,前提是value是一個(gè)數(shù)字
INCRBYFLOAT key increment 將 key 所儲(chǔ)存的值加上給定的浮點(diǎn)增量值臊诊,前提是value是一個(gè)數(shù)字
DECR key 將 key 中儲(chǔ)存的數(shù)字值減一,前提是value是一個(gè)數(shù)字
DECRBY key decrement key 所儲(chǔ)存的值減去給定的減量值斜脂,前提是value是一個(gè)數(shù)字

2抓艳、哈希

Redis的哈希是field和value之間的映射,即鍵值對(duì)的集合帚戳,所以特別適合用于存儲(chǔ)對(duì)象玷或。

Redis 中每個(gè) hash 最多可以存儲(chǔ) 2^32 - 1 鍵值對(duì)(40多億)。

例如片任,我們想在Redis中存儲(chǔ)一個(gè)用戶信息庐椒,包括用戶ID,用戶名蚂踊,郵箱地址三個(gè)字段:

127.0.0.1:6379>HMSET user_1 userId 123 userName clf email chenlongfei@163.com**

OK

127.0.0.1:6379> HGETALL user_1

1) "userId"

2) "123"

3) "userName"

4) "clf"

5) "email"

6) "chenlongfei@163.com"

命令格式 說明
HMSET key field1 value1 [field2 value2... ] Hash Multi Set)同時(shí)將多個(gè) field-value 對(duì)設(shè)置到哈希表 key 中
HMGET key field1 [field2...] 獲取所有給定字段的值
HSET key field value 將哈希表 key 中的字段 field 的值設(shè)為 value
HGET key field 獲取存儲(chǔ)在哈希表中指定字段的值
HGETALL key 獲取在哈希表中指定 key 的所有字段和值
HDEL key field2 [field2] 刪除一個(gè)或多個(gè)哈希表字段
HSETNX key field value 只有在字段 field 不存在時(shí),設(shè)置哈希表字段的值
HKEYS key 獲取所有哈希表中的字段
HVALS key 獲取哈希表中所有值
HEXISTS key field 查看哈希表 key 中笔宿,指定的字段是否存在
HLEN key 獲取哈希表中字段的數(shù)量
HINCRBY key field increment 為哈希表 key 中的指定字段的整數(shù)值加上增量
HINCRBYFLOAT key field increment 為哈希表 key 中的指定字段的浮點(diǎn)數(shù)值加上增量

3犁钟、列表

Redis列表是簡(jiǎn)單的字符串列表棱诱,按照插入順序排序。支持添加一個(gè)元素到列表頭部(左邊)或者尾部(右邊)的操作涝动。

一個(gè)列表最多可以包含 2^32 - 1 迈勋,即超過40億個(gè)元素。

例如醋粟,我們想用一個(gè)名為“Continents”的列表盛放五大洲的名字:

127.0.0.1:6379> LPUSH Continents Asia Africa America Oceania Antarctica

(integer) 5

127.0.0.1:6379> LRANGE Continents 0 4 --獲取下標(biāo)為0~4的元素

1) "Antarctica"

2) "Oceania"

3) "America"

4) "Africa"

5) "Asia"

Redis列表雖然名為列表靡菇,其實(shí)從特性上來講更像是棧,以最近放進(jìn)去的元素為頭米愿,以最早放進(jìn)去的元素為尾厦凤,所以,Redis列表的下標(biāo)呈倒序排列育苟。上例中依次放進(jìn)去的五個(gè)元素:Asia较鼓、Africa、America违柏、Oceania博烂、Antarctica,下標(biāo)分別為4漱竖、3禽篱、2、1馍惹、0躺率。這與Java中List的概念完全不一樣,需要特別注意讼积。

與棧類似肥照,當(dāng)執(zhí)行POP操作時(shí),Redis列表彈出的是最新放進(jìn)去的元素勤众,類似于棧頂元素舆绎。

Redis列表還支持一種阻塞式操作,比如BLPOP(Blockd List Pop之縮寫)们颜,移出并獲取列表的第一個(gè)元素吕朵,如果列表沒有元素(或列表不存在)會(huì)阻塞列表直到等待超時(shí)或發(fā)現(xiàn)可彈出元素為止。

例如窥突,我們對(duì)一個(gè)不存在的列表“myList”執(zhí)行BLPOP命令:

BLPOP myList 20 -- 彈出myList列表的第一個(gè)元素努溃,如果沒有,阻塞20秒

該客戶端會(huì)進(jìn)入阻塞狀態(tài)阻问,如果20秒之內(nèi)該列表存入了元素梧税,則彈出:

127.0.0.1:6379> BLPOP myList 20 --若無元素則進(jìn)入阻塞狀態(tài),限時(shí)20秒*

1) "myList"

2) "hello"

(6.20s)

如果超時(shí)后仍然沒有等到元素,則結(jié)束阻塞第队,返回nil:

127.0.0.1:6379> BLPOP myList 20

(nil)

(20.07s)

命令格式 說明
LPUSH key value1 [value2...] 將一個(gè)或多個(gè)值插入到列表頭部
LPOP key 移出并獲取列表的第一個(gè)元素
LPUSHX key value List Push if exist)將一個(gè)或多個(gè)值插入到已存在的列表頭部
LINDEX key index 通過索引獲取列表中的元素
LRANGE key start stop 獲取列表指定范圍內(nèi)的元素
LSET key index value 通過索引設(shè)置列表元素的值
LTRIM key start stop 只保留指定區(qū)間內(nèi)的元素哮塞,不在指定區(qū)間之內(nèi)的元素都將被刪除
RPOP key Rear Pop)移除并獲取列表最后一個(gè)元素
RPUSH key value1 [value2...] 將一個(gè)或多個(gè)值插入到列表尾部
RPUSHX key value 將一個(gè)或多個(gè)值插入到已存在的列表尾部
LREM key count value 從列表中刪除字段值為value的元素,刪除count的絕對(duì)值個(gè)value后結(jié)束凳谦,count > 0 從表頭刪除忆畅;count < 0 從表尾刪除;count=0 全部刪除
RPOPLPUSH source destination 移除列表的最后一個(gè)元素尸执,并將該元素添加到另一個(gè)列表并返回
BLPOP key1 [key2... ] timeout 移出并獲取列表的第一個(gè)元素家凯, 如果列表沒有元素會(huì)阻塞列表直到等待超時(shí)或發(fā)現(xiàn)可彈出元素為止,如果timeout為0則一直等待下去
BRPOP key1 [key2... ] timeout 移出并獲取列表的最后一個(gè)元素如失, 如果列表沒有元素會(huì)阻塞列表直到等待超時(shí)或發(fā)現(xiàn)可彈出元素為止也拜,如果timeout為0則一直等待下去
LINSERT key BEFORE | AFTER pivot value 在key 列表中尋找pivot撒桨,并在pivot值之前|之后插入value
LLEN key 獲取列表長(zhǎng)度

4、集合

Redis集合是String類型的無序集合。集合成員是唯一的主儡,這就意味著集合中不能出現(xiàn)重復(fù)的數(shù)據(jù)棘脐。

Redis集合是通過哈希表實(shí)現(xiàn)的恋沃,所以添加裆熙,刪除,查找的復(fù)雜度都是O(1)偎快。

集合中最大的成員數(shù)為 2^32 - 1 冯乘,即每個(gè)集合最多可存儲(chǔ)40多億個(gè)成員。

集合的一大特點(diǎn)就是不能有重復(fù)元素晒夹,如果插入重復(fù)元素裆馒,Redis會(huì)忽略該操作:

127.0.0.1:6379> SADD direction east west south north

(integer) 4

127.0.0.1:6379> SMEMBERS direction

1) "west"

2) "east"

3) "north"

4) "south"

127.0.0.1:6379> SADD direction east

(integer) 0 -- east元素已經(jīng)存在,該操作無效

127.0.0.1:6379> SMEMBERS direction

1) "west"

2) "east"

3) "north"

4) "south"

Redis集合還有兩大特點(diǎn)丐怯,一是支持隨機(jī)獲取元素喷好,二是支持集合間的取差集、交集與并集操作读跷。

命令格式 說明
SADD key member1 [member2…] 向集合添加一個(gè)或多個(gè)成員
SREM key member1 [member2…] 移除集合中一個(gè)或多個(gè)成員
SPOP key 移除并返回集合中的一個(gè)隨機(jī)元素
SMEMBERS key 返回集合中的所有成員
SRANDMEMBER key [count] 返回集合中count個(gè)隨機(jī)元素梗搅,如count為空,則只返回一個(gè)
SCARD key Set Cardinality)返回集合中的元素總數(shù)
SISMEMBER key member 判membe元素是否是集key 的成員
SMOVE source destination member 將member元素從source集合移動(dòng)到destination集合
SDIFF key1 [key2…] 返回給定所有集合的差集效览,即以key1為基準(zhǔn)无切,返回key1有且[key2...]沒有的元素
SDIFFSTORE destination key1 [key2…] 返回給定所有集合的差集并存儲(chǔ)在destination中
SINTER key1 [key2…] 返回給定所有集合的交集
SINTERSTORE destination key1 [key2…] 返回給定所有集合的交集并存儲(chǔ)在destination中
SUNION key1 [key2…] 返回所有給定集合的并集
SUNIONSTORE destination key1 [key2…] 所有給定集合的并集存儲(chǔ)在destination集合中

5、有序列表

Redis 有序集合和集合一樣也是String類型元素的集合丐枉,且不允許重復(fù)的成員哆键。

不同的是每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè)double類型的分?jǐn)?shù)。Redis正是通過分?jǐn)?shù)來為集合中的成員進(jìn)行從小到大的排序瘦锹。有序集合的成員是唯一的籍嘹,但分?jǐn)?shù)(score)卻可以重復(fù)闪盔。

集合是通過哈希表實(shí)現(xiàn)的,所以添加辱士,刪除锭沟,查找的復(fù)雜度都是O(1)。

集合中最大的成員數(shù)為 2^32 - 1 识补,即每個(gè)集合最多可存儲(chǔ)40多億個(gè)成員。

例如辫红,凭涂、使用有序列表來存儲(chǔ)學(xué)生的成績(jī)單:

127.0.0.1:6379> ZADD scoreList 82 Tom

(integer) 1

127.0.0.1:6379> ZADD scoreList 65.5 Jack

(integer) 1

127.0.0.1:6379> ZADD scoreList 43.5 Rubby

(integer) 1

127.0.0.1:6379> ZADD scoreList 99 Winner

(integer) 1

127.0.0.1:6379> ZADD scoreList 78 Linda

(integer) 1

127.0.0.1:6379> ZRANGE scoreList 0 100 WITHSCORES --獲取名次在0~100之間的記錄

1) "Rubby"

2) "43.5"

3) "Jack"

4) "65.5"

5) "Linda"

6) "78"

7) "Tom"

8) "82"

9) "Winner"

10) "99"

需要注意的是,Redis有序集合是默認(rèn)升序的贴妻,score越低排名越靠前切油,即score越低的元素下標(biāo)越小。

命令格式 說明
ZADD key score1 member1 [score2 member2 ...] 添加一個(gè)或多個(gè)成員到有序集合名惩,或者如果它已經(jīng)存在更新其分?jǐn)?shù)
ZRANGE key start stop [WITHSCORES] 把集合排序后澎胡,返回名次在[start,stop]之間的元素娩鹉。 WITHSCORES是把score也打印出來
ZREVRANGE key start stop [WITHSCORES] 倒序排列(分?jǐn)?shù)越大排名越靠前)攻谁,返回名次在[start,stop]之間的元素
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset n] 集合(升序)排序后取score在[min, max]內(nèi)的元素,并跳過offset個(gè)弯予,取出n個(gè)
ZREM key member [member ...] 從有序集合中刪除一個(gè)或多個(gè)成員
ZRANK key member 確定member在集合中的升序名次
ZREVRANK key member 確定member在集合中的降序名次
ZSCORE key member 獲取member的分?jǐn)?shù)
ZCARD key 獲取有序集合中成員的數(shù)量
ZCOUNT key min max 計(jì)算分?jǐn)?shù)在min與max之間的元素總數(shù)
ZINCRBY key increment member 給member的分?jǐn)?shù)增加increment
ZREMRANGEBYRANK key start stop 移除名次在start與stop之間的元素
ZREMRANGEBYSCORE key min max 移除分?jǐn)?shù)在min與max之間的元素

6戚宦、存儲(chǔ)結(jié)構(gòu)

Redis的一種對(duì)象類型可以有不同的存儲(chǔ)結(jié)構(gòu)來實(shí)現(xiàn),從而同時(shí)兼顧性能和內(nèi)存锈嫩。

1.png

字典又名映射(map)或關(guān)聯(lián)數(shù)組(associative array)受楼, 是一種抽象數(shù)據(jù)結(jié)構(gòu), 由一集鍵值對(duì)(key-value pairs)組成呼寸, 各個(gè)鍵值對(duì)的鍵各不相同艳汽, 程序可以添加新的鍵值對(duì)到字典中,或者基于鍵進(jìn)行查找对雪、更新或刪除等操作河狐。

字典在 Redis 中的應(yīng)用廣泛, 使用頻率可以說和 SDS 以及雙端鏈表不相上下慌植,基本上各個(gè)功能模塊都有用到字典的地方甚牲。

字典的主要用途有以下兩個(gè):

  • 實(shí)現(xiàn)數(shù)據(jù)庫鍵空間(key space)
  • 用作 Hash 類型鍵的底層實(shí)現(xiàn)之一

Redis字典采用Hash表實(shí)現(xiàn),針對(duì)碰撞問題蝶柿,采用的方法為“鏈地址法”丈钙,即將多個(gè)哈希值相同的節(jié)點(diǎn)串連在一起,從而解決沖突問題交汤。

“鏈地址法”的問題在于當(dāng)碰撞劇烈時(shí)雏赦,性能退化嚴(yán)重劫笙,例如:當(dāng)有n個(gè)數(shù)據(jù),m個(gè)槽位星岗,如果m=1填大,則整個(gè)Hash表退化為鏈表,查詢復(fù)雜度O(n)俏橘。為了避免Hash碰撞攻擊允华,Redis隨機(jī)化了Hash表種子。

Redis的方案是雙buffer寥掐,正常流程使用一個(gè)buffer靴寂,當(dāng)發(fā)現(xiàn)碰撞劇烈(判斷依據(jù)為當(dāng)前槽位數(shù)和Key數(shù)的對(duì)比),分配一個(gè)更大的buffer召耘,然后逐步將數(shù)據(jù)從老的buffer遷移到新的buffer百炬。一般情況下只使用 0 號(hào)哈希表,只有在 rehash 進(jìn)行時(shí)污它,才會(huì)同時(shí)使用 0 號(hào)和 1 號(hào)哈希表剖踊。

redisObject是真正存儲(chǔ)redis各種類型的結(jié)構(gòu),在Redis源碼的redis.h文件中衫贬,定義了這些結(jié)構(gòu):

/* A redis object, that is a type able to hold a string / list / set */

/* The actual Redis Object */

\#define REDIS_LRU_BITS 24

\#define REDIS_LRU_CLOCK_MAX ((1<<REDIS_LRU_BITS)-1) /* Max value of obj->lru */

\#define REDIS_LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */

typedef struct redisObject {

  unsigned type:4;

  unsigned encoding:4;

  unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */

  int refcount;

  void *ptr;

} robj;

其中type即Redis支持的邏輯類型德澈,包括:

/* Object types */

\#define REDIS_STRING 0

\#define REDIS_LIST 1

\#define REDIS_SET 2

\#define REDIS_ZSET 3

\#define REDIS_HASH 4

即前面所列舉的五種數(shù)據(jù)類型。type定義的只是邏輯類型祥山,encoding才是物理存儲(chǔ)方式圃验,**一種邏輯類型可以使用不同的存儲(chǔ)方式**,包括:

/* Objects encoding. Some kind of objects like Strings and Hashes can be

 \* internally represented in multiple ways. The 'encoding' field of the object

 \* is set to one of this fields for this object. */

\#define REDIS_ENCODING_RAW 0   /* Raw representation */

\#define REDIS_ENCODING_INT 1   /* Encoded as integer */

\#define REDIS_ENCODING_HT 2   /* Encoded as hash table */

\#define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */

\#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */

\#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */

\#define REDIS_ENCODING_INTSET 6 /* Encoded as intset */

\#define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */

\#define REDIS_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
  • REDIS_ENCODING_RAW 即原生態(tài)的存儲(chǔ)結(jié)構(gòu)缝呕,就是以字符串形式存儲(chǔ)澳窑,字符串類型在redis中用sds(simple dynamic string)封裝,主要為了解決長(zhǎng)度計(jì)算和追加效率的問題供常。
  • REDIS_ENCODING_INT 代表整數(shù)摊聋,以long型存儲(chǔ)。
  • REDIS_ENCODING_HT 代表哈希表(Hash Table)栈暇,以哈希表結(jié)構(gòu)存儲(chǔ)麻裁,與字典的實(shí)現(xiàn)方法一致。
  • REDIS_ENCODING_ZIPMAP 其實(shí)質(zhì)是用一個(gè)字符串?dāng)?shù)組來依次保存key和value源祈,查詢時(shí)是依次遍列每個(gè) key-value 對(duì)煎源,直到查到為止。
    2.png
  • REDIS_ENCODING_LINKEDLIST 代表鏈表香缺,以典型的鏈表結(jié)構(gòu)存儲(chǔ)手销。
  • REDIS_ENCODING_ZIPLIST 代表一種雙端列表,且通過特殊的格式定義图张,壓縮內(nèi)存適用锋拖,以時(shí)間換空間诈悍。ZIPLIST適合小數(shù)據(jù)量的讀場(chǎng)景,不適合大數(shù)據(jù)量的多寫/刪除場(chǎng)景兽埃。
    3.png
  • REDIS_ENCODING_INTSET 是用一個(gè)有序的整數(shù)數(shù)組來實(shí)現(xiàn)的侥钳。
    4.png
  • REDIS_ENCODING_SKIPLIST 同時(shí)采用字典和有序集兩種數(shù)據(jù)結(jié)構(gòu)來保存數(shù)據(jù)元素。跳躍表(SkipList)是一個(gè)特殊的鏈表柄错,相比一般的鏈表舷夺,有更高的查找效率,其效率可比擬于二叉查找樹售貌。一張關(guān)于跳表和跳表搜索過程如下圖:
    5.png

    在圖中冕房,需要尋找 68,在給出的查找過程中趁矾,利用跳表數(shù)據(jù)結(jié)構(gòu)優(yōu)勢(shì),只比較了 3次给僵,橫箭頭不比較毫捣,豎箭頭比較。由此可見帝际,跳表預(yù)先間隔地保存了有序鏈表中的節(jié)點(diǎn)蔓同,從而在查找過程中能達(dá)到類似于二分搜索的效果,而二分搜索思想就是通過比較中點(diǎn)數(shù)據(jù)放棄另一半的查找蹲诀,從而節(jié)省一半的查找時(shí)間斑粱。
    缺點(diǎn)即浪費(fèi)了空間,自古空間和時(shí)間兩難全脯爪。
  • REDIS_ENCODING_EMBSTR 代表使用embstr 編碼的簡(jiǎn)單動(dòng)態(tài)字符串则北。好處有如下幾點(diǎn): embstr的創(chuàng)建只需分配一次內(nèi)存,而raw為兩次(一次為sds分配對(duì)象痕慢,另一次為objet分配對(duì)象尚揣,embstr省去了第一次)。相對(duì)地掖举,釋放內(nèi)存的次數(shù)也由兩次變?yōu)橐淮慰炱mbstr的objet和sds放在一起,更好地利用緩存帶來的優(yōu)勢(shì)塔次。需要注意的是方篮,Redis并未提供任何修改embstr的方式,即embstr是只讀的形式励负。對(duì)embstr的修改實(shí)際上是先轉(zhuǎn)換為raw再進(jìn)行修改藕溅。

6.1 字符串的存儲(chǔ)結(jié)構(gòu)

Redis的所有的key都采用字符串保存,而值可以是字符串熄守,列表蜈垮,哈希耗跛,集合和有序集合對(duì)象的其中一種。

字符串存儲(chǔ)的邏輯類型即REDIS_STRING攒发,其物理實(shí)現(xiàn)(enconding)可以為 REDIS_ENCODING_INT调塌、REDIS_ENCODING_EMBSTR 或 REDIS_ENCODING_RAW。

首先惠猿,如果可以使用REDIS_ENCODING_EMBSTR編碼羔砾,Redis首選REDIS_ENCODING_EMBSTR保存,偶妖;其次姜凄,如果可以轉(zhuǎn)換,Redis會(huì)嘗試將一個(gè)字符串轉(zhuǎn)化為L(zhǎng)ong趾访,保存為REDIS_ENCODING_INT态秧,如“26”、“180”等扼鞋;最后申鱼,Redis會(huì)保存為REDIS_ENCODING_RAW,如“chenlongfei”云头、“Redis”等捐友。

6.2 哈希的存儲(chǔ)結(jié)構(gòu)

REDIS_HASH可以有兩種encoding方式: REDIS_ENCODING_ZIPLIST 和 REDIS_ENCODING_HT

Hash表默認(rèn)的編碼格式為REDIS_ENCODING_ZIPLIST,在收到來自用戶的插入數(shù)據(jù)的命令時(shí):

(1)調(diào)用hashTypeTryConversion函數(shù)檢查鍵/值的長(zhǎng)度大于配置的hash_max_ziplist_value(默認(rèn)64)

(2)調(diào)用hashTypeSet判斷節(jié)點(diǎn)數(shù)量大于配置的hash_max_ziplist_entries (默認(rèn)512)

以上任意條件滿足則將Hash表的數(shù)據(jù)結(jié)構(gòu)從REDIS_ENCODING_ZIPLIST轉(zhuǎn)為REDIS_ENCODING_HT溃槐。

6.3 列表的存儲(chǔ)結(jié)構(gòu)

REDIS_SET有兩種encoding方式匣砖,REDIS_ENCODING_ZIPLIST和REDIS_ENCODING_LINKEDLIST。

列表的默認(rèn)編碼格式為REDIS_ENCODING_ZIPLIST昏滴,當(dāng)滿足以下條件時(shí)猴鲫,編碼格式轉(zhuǎn)換為REDIS_ENCODING_LINKEDLIST:

(1)元素大小大于list-max-ziplist-value(默認(rèn)64)

(2)元素個(gè)數(shù)大于 配置的list-max-ziplist-entries(默認(rèn)512)

6.4 集合的存儲(chǔ)結(jié)構(gòu)

REDIS_SET有兩種encoding方式: REDIS_ENCODING_INTSET 和 REDIS_ENCODING_HT。

集合的元素類型和數(shù)量決定了encoding方式谣殊,默認(rèn)采用REDIS_ENCODING_INTSET 变隔,當(dāng)滿足以下條件時(shí),轉(zhuǎn)換為REDIS_ENCODING_HT:

(1)元素類型不是整數(shù)

(2)元素個(gè)數(shù)超過配置的set-max-intset-entries(默認(rèn)512)

6.5 有序列表的存儲(chǔ)結(jié)構(gòu)

REDIS_ZSET有兩種encoding方式: REDIS_ENCODING_ZIPLIST(同上)和 REDIS_ENCODING_SKIPLIST蟹倾。

由于有序集合每一個(gè)元素包括:<member匣缘,score>兩個(gè)屬性,為了保證對(duì)member和score都有很好的查詢性能鲜棠,REDIS_ENCODING_SKIPLIST同時(shí)采用字典和有序集兩種數(shù)據(jù)結(jié)構(gòu)來保存數(shù)據(jù)元素肌厨。字典和有序集通過指針指向同一個(gè)數(shù)據(jù)節(jié)點(diǎn)來避免數(shù)據(jù)冗余。

字典中使用member作為key豁陆,score作為value柑爸,從而保證在O(1)時(shí)間對(duì)member的查找跳躍表基于score做排序,從而保證在 O(logN) 時(shí)間內(nèi)完成通過score對(duì)memer的查詢盒音。

有序集合默認(rèn)也是采用REDIS_ENCODING_ZIPLIST的實(shí)現(xiàn)表鳍,當(dāng)滿足以下條件時(shí)馅而,轉(zhuǎn)換為REDIS_ENCODING_SKIPLIST:

(1)數(shù)據(jù)元素個(gè)數(shù)超過配置zset_max_ziplist_entries 的值(默認(rèn)值為 128 )

(2)新添加元素的 member 的長(zhǎng)度大于配置的zset_max_ziplist_value 的值(默認(rèn)值為 64 )

6.6 總結(jié)

針對(duì)同一種數(shù)據(jù)類型,Redis會(huì)根據(jù)元素類型/大小/個(gè)數(shù)采用不同的編碼方式譬圣,不同的編碼方式在內(nèi)存使用效率/查詢效率上差距巨大瓮恭,可以通過配置文件調(diào)整參數(shù)來達(dá)到最優(yōu)。

RedisObject 實(shí)現(xiàn)方式一 實(shí)現(xiàn)方式二 實(shí)現(xiàn)方式三
字符串 REDIS_ENCODING_INT REDIS_ENCODING_EMBSTR REDIS_ENCODING_RAW
哈希 REDIS_ENCODING_ZIPLIST REDIS_ENCODING_HT
列表 REDIS_ENCODING_ZIPLIST REDIS_ENCODING_LINKEDLIST
集合 REDIS_ENCODING_INTSET REDIS_ENCODING_HT
有序集合 REDIS_ENCODING_ZIPLIST REDIS_ENCODING_SKIPLIST
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末厘熟,一起剝皮案震驚了整個(gè)濱河市屯蹦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绳姨,老刑警劉巖登澜,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異飘庄,居然都是意外死亡脑蠕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門跪削,熙熙樓的掌柜王于貴愁眉苦臉地迎上來空郊,“玉大人,你說我怎么就攤上這事切揭。” “怎么了锁摔?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵廓旬,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我谐腰,道長(zhǎng)孕豹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任十气,我火速辦了婚禮励背,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘砸西。我一直安慰自己叶眉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布芹枷。 她就那樣靜靜地躺著衅疙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸳慈。 梳的紋絲不亂的頭發(fā)上饱溢,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音走芋,去河邊找鬼绩郎。 笑死潘鲫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肋杖。 我是一名探鬼主播溉仑,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼兽愤!你這毒婦竟也來了彼念?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤浅萧,失蹤者是張志新(化名)和其女友劉穎逐沙,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洼畅,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吩案,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帝簇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片徘郭。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖丧肴,靈堂內(nèi)的尸體忽然破棺而出残揉,到底是詐尸還是另有隱情,我是刑警寧澤芋浮,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布抱环,位于F島的核電站,受9級(jí)特大地震影響纸巷,放射性物質(zhì)發(fā)生泄漏镇草。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一瘤旨、第九天 我趴在偏房一處隱蔽的房頂上張望梯啤。 院中可真熱鬧,春花似錦存哲、人聲如沸因宇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽羽嫡。三九已至,卻和暖如春肩袍,著一層夾襖步出監(jiān)牢的瞬間杭棵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留魂爪,地道東北人先舷。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像滓侍,于是被迫代替她去往敵國和親蒋川。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350