哈希
- 幾乎所有的編程語言都提供了哈希(hash)類型,它們的叫法可能是哈希、字
典、關(guān)聯(lián)數(shù)組。在Redis中,哈希類型是指鍵值本身又是一個(gè)鍵值對(duì)結(jié)構(gòu),形如
value={{field,value},...{fieldN,valueN}}。
-
命令
(1)設(shè)置值
hset key field value下面為user:1添加一堆field-value:
127.0.0.1:6379> hset user:1 name tom (integer) 1如果設(shè)置成功會(huì)返回1,反之會(huì)返回0。此外Redis提供了hsetnx命令,它們的
關(guān)系就像set和setnx命令一樣,只不過作用域由鍵變?yōu)閒ield。(2)獲取值
hget key field例如,下面操作獲取user:1的那么域(屬性)對(duì)應(yīng)的值:
127.0.0.1:6379> hget user:1 name "tom"如果鍵或field不存在,會(huì)返回null
127.0.0.1:6379> hget user:2 name (nil) 127.0.0.1:6379> hget user:1 age (nil)(3)刪除field
hdel key field [field...]hdel會(huì)刪除一個(gè)或多個(gè)field,返回結(jié)果為成功刪除field的個(gè)數(shù),例如:
127.0.0.1:6379> hdel user:1 name (integer) 1 127.0.0.1:6379> hdel user:1 age (integer) 0(4)計(jì)算field個(gè)數(shù)
hlen key例如user:1有3個(gè)field
127.0.0.1:6379> hset user:1 name tom (integer) 1 127.0.0.1:6379> hset user:1 age 23 (integer) 1 127.0.0.1:6379> hset user:1 city tianjin (integer) 1 127.0.0.1:6379> hlen user:1 (integer) 3(5)批量設(shè)置或獲取field-value
hmget key field [field ...] hmset key field value [field value ...]hmset和hmget分別是批量設(shè)置和獲取field-value,hmset需要的參數(shù)是key和
多對(duì)field-value,hmget需要的參數(shù)是key和多個(gè)field。例如:127.0.0.1:6379> hmset user:1 name mike age 12 city tianjin OK 127.0.0.1:6379> hmget user:1 name city 1) "mike" 2) "tianjin"(6)判斷field是否存在
hexists key filed例如,user:1包含name域,所以返回結(jié)果為1,不包含時(shí)返回0:
127.0.0.1:6379> hexists user:1 name (integer) 1(7)獲取所有filed
hkeys keyhkeys命令應(yīng)該叫hfields更為恰當(dāng),他返回制定哈希鍵所有的field,例如:
127.0.0.1:6379> hkeys user:1 1) "name" 2) "age" 3) "city"(8)獲取所有value
hvals key下面操作獲取user:1全部value:
127.0.0.1:6379> hvals user:1 1) "mike" 2) "12" 3) "tianjin"(9)獲取所有的field-value
hgetall key下面操作獲取user:1所有的field-value:
127.0.0.1:6379> hgetall user:1 1) "name" 2) "mike" 3) "age" 4) "12" 5) "city" 6) "tianjin"(10)hinerby hincybyfloat
hincrby key field
hincrbyfloat key fieldhincrby和hincrbyfloat,就像incrbyhy和incrbyfloat命令一樣,但是它們
的作用域是field。(11)計(jì)算value的字符串長度
hstrlen key field例如hget user:1 name的value是tom,那么hstrlen的返回結(jié)果是3:
127.0.0.1:6379> hstrlen user:1 name (integer) 3下表是哈希類型命令的時(shí)間復(fù)雜度
命令 時(shí)間復(fù)雜度 hset key field value O(1) hget key field O(1) hdel key field [field ...] O(k),k是field的個(gè)數(shù) hlen key O(1) hgetall key O(n),n是field的總數(shù) hmget field [field ...] O(k),k是field的個(gè)數(shù) hmset field value [field value ...] O(k),k是field的個(gè)數(shù) hexists key field O(1) hkeys key O(n),n是field的總數(shù) hvals key O(n),n是field的總數(shù) hstenx key field value O(1) hincrby key field increment O(1) hincrbyfloat key filed increment O(1) hstrlen key field O(1)
-
內(nèi)部編碼
哈希類型的編碼有兩種:
ziplist(壓縮列表):當(dāng)哈希類型元素個(gè)數(shù)小于
hash-max-ziplist-entries配置(默認(rèn)512個(gè))、同時(shí)所有值都小于
hash-max-ziplist-value配置(默認(rèn)64字節(jié))是,Redis會(huì)使用ziplist作為
哈希的內(nèi)部實(shí)現(xiàn),ziplist使用更加緊湊的結(jié)構(gòu)實(shí)現(xiàn)多個(gè)元素的連續(xù)存儲(chǔ),所以
在節(jié)省內(nèi)存方面比hashtable更加優(yōu)秀。hashtable(哈希表):當(dāng)哈希類型無法滿足ziplist的條件時(shí),Redis會(huì)使
用hashtable作為哈希的內(nèi)部實(shí)現(xiàn),因?yàn)榇藭r(shí)ziplist的讀寫效率會(huì)下降,而
hashtable的讀寫時(shí)間復(fù)雜度是O(1)。
下面的實(shí)例演示了哈希類型的內(nèi)部編碼,以及相應(yīng)的變化。
1)當(dāng)field個(gè)數(shù)比較少且沒有大的value時(shí),內(nèi)部編碼為ziplist:
127.0.0.1:6379> hmset hashkey f1 v1 f2 v2 OK 127.0.0.1:6379> objet encoding hashkey "ziplist"2.1)當(dāng)有value大于64字節(jié),內(nèi)部編碼會(huì)由ziplist變?yōu)閔ashtable:
127.0.0.1:6379> hset hashkey f3 "one string is bigger than 64 byte ...忽略..." OK 127.0.0.1:6379> object encoding hashkey "hashtable"2.2)當(dāng)field個(gè)數(shù)超過512,內(nèi)部編碼也會(huì)有ziplist變?yōu)閔ashtable:
127.0.0.1:6379> hmset hashkey f1 v1 f2 v2 f3 v3 ...忽略... f513 v513 OK 127.0.0.1:6379> object encoding hashkey "hashtable" -
使用場(chǎng)景
下表為關(guān)系型數(shù)據(jù)表記錄的兩條用戶信息,用戶的屬性作為表的列,每條用戶信
息作為行id name age city 1 tom 23 beijing 2 mike 30 tianjin 如果將其用哈希類型存儲(chǔ),如下表
user:1
field value id 1 name tom age 23 city beijing user:2
field value id 2 name mike age 30 city tianjing 相比于使用字符串序列化緩存用戶信息,哈希類型變得更加直觀,并且在更新操
作上會(huì)更加便捷??梢詫⒚總€(gè)用戶的id定義為鍵后綴,多對(duì)field-value對(duì)應(yīng)每
個(gè)用戶的屬性,類似如下偽代碼:UserInfo getUserInfo(long id){ //用戶id作為key后綴 userRedisKey = "user:info" + id; //使用hgetall獲取所有用戶信息映射關(guān)系 userInfoMap = redis.hgetAll(userRedisKey); UserInfo userInfo; if(userInfoMap != null){ //將映射關(guān)系轉(zhuǎn)換為UserInfo userInfo = transferMapToUserInfo(userInfoMap); }else{ //從MySQL中獲取用戶信息 userInfo = mysql.get(id); //將userInfo變?yōu)橛成潢P(guān)系使用hmset保存到Redis中 redis.hmset(userRedisKey, transferUserInfoToMap(userInfo)); //添加過期時(shí)間 redis.expire(userRedisKey, 3600); } return usreInfo; }但是需要注意的是哈希類型和關(guān)系型數(shù)據(jù)庫有兩點(diǎn)不同之處:
- 哈希類型是稀疏的,而關(guān)系型數(shù)據(jù)庫是完全結(jié)構(gòu)化的,例如哈希類型每個(gè)鍵可
以有不同的field,而關(guān)系型數(shù)據(jù)庫一旦添加新的列,所有行都要為其設(shè)置值
(即使為NULL),如下表:
id name favor city age gender 1 tom sprot beijing NULL NULL 2 mike NULL NULL 30 male user:1
field value id 1 name tom favor sport city beijing user:2
field value id 2 name mike age 30 gender male 開發(fā)人員需要將兩者的特點(diǎn)搞清楚,才能在適合的場(chǎng)景使用適合的技術(shù)。到目前
為止,我們已經(jīng)能夠用三種方法緩存用戶信息,下面給出三種方案的實(shí)現(xiàn)方法和
優(yōu)缺點(diǎn)分析。1)原聲字符串類型:每個(gè)屬性一個(gè)鍵。
set user:1:name tom set user:1:age 23 set user:1:city beijing優(yōu)點(diǎn):簡單直觀,每個(gè)屬性都支持更新操作。
缺點(diǎn):占用過多的鍵,內(nèi)存占用量較大,同時(shí)用戶信息內(nèi)聚性比較差,所以此種
方案一般不會(huì)在生產(chǎn)環(huán)境使用。2)序列化字符串類型:將用戶信息序列化后用一個(gè)鍵保存。
set user:1 serialize(usreInfo)優(yōu)點(diǎn):簡化編程,如果合理的使用序列化可以提高內(nèi)存的使用效率。
缺點(diǎn):序列化和反序列化有一定的開銷,同時(shí)每次更新屬性都需要把全部數(shù)據(jù)取
出進(jìn)行反序列化,更新后在序列化到Redis中。3)哈希類型:每個(gè)用戶屬性使用一堆field-value,但是只用一個(gè)鍵保存。
hmset user:1 name tom age 23 city beijing優(yōu)點(diǎn):簡單直觀,如果使用合理可以減少內(nèi)存空間的使用。
缺點(diǎn):要控制哈希在ziplist和hashtable兩種內(nèi)部編碼的轉(zhuǎn)換,hashtable會(huì)消耗更多內(nèi)存。 - 哈希類型是稀疏的,而關(guān)系型數(shù)據(jù)庫是完全結(jié)構(gòu)化的,例如哈希類型每個(gè)鍵可