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)存锈嫩。
字典又名映射(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 |