edis 常用數(shù)據(jù)類型
Redis 最為常用的數(shù)據(jù)類型主要有以下五種:
• String
• Hash
• List
• Set
• Sorted set
在具體描述這幾種數(shù)據(jù)類型之前,我們先通過(guò)一張圖了解下 Redis 內(nèi)部?jī)?nèi)存管理中是如何描述這些不同數(shù)據(jù)類型的:

首先 Redis 內(nèi)部使用一個(gè) redisObject 對(duì)象來(lái)表示所有的 key 和 value,redisObject 最主要的信息如上圖所示:type 代表一個(gè) value 對(duì)象具體是何種數(shù)據(jù)類型,encoding 是不同數(shù)據(jù)類型在 redis 內(nèi)部的存儲(chǔ)方式,比如:type=string 代表 value 存儲(chǔ)的是一個(gè)普通字符串,那么對(duì)應(yīng)的 encoding 可以是 raw 或者是 int,如果是 int 則代表實(shí)際 redis 內(nèi)部是按數(shù)值型類存儲(chǔ)和表示這個(gè)字符串的,當(dāng)然前提是這個(gè)字符串本身可以用數(shù)值表示,比如:”123″ “456”這樣的字符串。
這里需要特殊說(shuō)明一下 vm 字段,只有打開(kāi)了 Redis 的虛擬內(nèi)存功能,此字段才會(huì)真正的分配內(nèi)存,該功能默認(rèn)是關(guān)閉狀態(tài)的,該功能會(huì)在后面具體描述。通過(guò)上圖我們可以發(fā)現(xiàn) Redis 使用 redisObject 來(lái)表示所有的 key/value 數(shù)據(jù)是比較浪費(fèi)內(nèi)存的,當(dāng)然這些內(nèi)存管理成本的付出主要也是為了給 Redis 不同數(shù)據(jù)類型提供一個(gè)統(tǒng)一的管理接口,實(shí)際作者也提供了多種方法幫助我們盡量節(jié)省內(nèi)存使用,我們隨后會(huì)具體討論。
下面我們先來(lái)逐一的分析下這五種數(shù)據(jù)類型的使用和內(nèi)部實(shí)現(xiàn)方式:
String
常用命令:
set,get,decr,incr,mget 等。
應(yīng)用場(chǎng)景:
String 是最常用的一種數(shù)據(jù)類型,普通的 key/value 存儲(chǔ)都可以歸為此類,這里就不所做解釋了。
實(shí)現(xiàn)方式:
String 在 redis 內(nèi)部存儲(chǔ)默認(rèn)就是一個(gè)字符串,被 redisObject 所引用,當(dāng)遇到 incr,decr 等操作時(shí)會(huì)轉(zhuǎn)成數(shù)值型進(jìn)行計(jì)算,此時(shí) redisObject 的 encoding 字段為int。
Hash
常用命令:
hget,hset,hgetall 等。
應(yīng)用場(chǎng)景:
我們簡(jiǎn)單舉個(gè)實(shí)例來(lái)描述下 Hash 的應(yīng)用場(chǎng)景,比如我們要存儲(chǔ)一個(gè)用戶信息對(duì)象數(shù)據(jù),包含以下信息:
用戶 ID 為查找的 key,存儲(chǔ)的 value 用戶對(duì)象包含姓名,年齡,生日等信息,如果用普通的 key/value 結(jié)構(gòu)來(lái)存儲(chǔ),主要有以下2種存儲(chǔ)方式:

第一種方式將用戶 ID 作為查找 key,把其他信息封裝成一個(gè)對(duì)象以序列化的方式存儲(chǔ),這種方式的缺點(diǎn)是,增加了序列化/反序列化的開(kāi)銷,并且在需要修改其中一項(xiàng)信息時(shí),需要把整個(gè)對(duì)象取回,并且修改操作需要對(duì)并發(fā)進(jìn)行保護(hù),引入CAS等復(fù)雜問(wèn)題。

第二種方法是這個(gè)用戶信息對(duì)象有多少成員就存成多少個(gè) key-value 對(duì)兒,用用戶 ID +對(duì)應(yīng)屬性的名稱作為唯一標(biāo)識(shí)來(lái)取得對(duì)應(yīng)屬性的值,雖然省去了序列化開(kāi)銷和并發(fā)問(wèn)題,但是用戶 ID 為重復(fù)存儲(chǔ),如果存在大量這樣的數(shù)據(jù),內(nèi)存浪費(fèi)還是非常可觀的。
那么 Redis 提供的 Hash 很好的解決了這個(gè)問(wèn)題,Redis 的 Hash 實(shí)際是內(nèi)部存儲(chǔ)的 Value 為一個(gè) HashMap,并提供了直接存取這個(gè) Map 成員的接口,如下圖:

也就是說(shuō),Key 仍然是用戶 ID,value 是一個(gè) Map,這個(gè) Map 的 key 是成員的屬性名,value 是屬性值,這樣對(duì)數(shù)據(jù)的修改和存取都可以直接通過(guò)其內(nèi)部 Map 的 Key(Redis 里稱內(nèi)部 Map 的 key 為 field),也就是通過(guò) key(用戶 ID) + field(屬性標(biāo)簽)就可以操作對(duì)應(yīng)屬性數(shù)據(jù)了,既不需要重復(fù)存儲(chǔ)數(shù)據(jù),也不會(huì)帶來(lái)序列化和并發(fā)修改控制的問(wèn)題。很好的解決了問(wèn)題。
這里同時(shí)需要注意,Redis 提供了接口(hgetall)可以直接取到全部的屬性數(shù)據(jù),但是如果內(nèi)部 Map 的成員很多,那么涉及到遍歷整個(gè)內(nèi)部 Map 的操作,由于 Redis 單線程模型的緣故,這個(gè)遍歷操作可能會(huì)比較耗時(shí),而另其它客戶端的請(qǐng)求完全不響應(yīng),這點(diǎn)需要格外注意。