一、概述
在Redis中,hash類(lèi)型是指鍵值本身又是一個(gè)鍵值對(duì)結(jié)構(gòu),形如 value = {{field1,value1},....,{fieldN,valueN}},注意區(qū)分在哈希類(lèi)型中的映射關(guān)系(field-value)這里的value指的是field對(duì)應(yīng)的值,而不是key對(duì)應(yīng)的值。
hash類(lèi)型與String類(lèi)型對(duì)比如下:

二、常用命令
- 設(shè)置值
- hset key field value:如果設(shè)置成功會(huì)返回1,反之返回0,另外Redis提供了hsetnx命令,其關(guān)系就像set與setnx命令一樣,只是作用域由鍵變成field;
- hmset key field [field value]:批量設(shè)置field-value;
- 獲取值
- hget key field:獲取單個(gè)value,如果key或field不存在則返回nil;
- hmget key field :批量獲取field-value;
- hkeys key:獲取所有的field;
- hvals key:獲取所有的value;
- hgetall key:獲取所有的field-value;
- 刪除field
- hdel key field [field...]:刪除一個(gè)或者多個(gè)field,返回結(jié)果為成功刪除field的個(gè)數(shù);
- 其他命令
- hlen key :計(jì)算field的個(gè)數(shù);
- hexists key field:判斷field是否存在;
三、內(nèi)部編碼
hash類(lèi)型的內(nèi)部編碼有兩種:
- ziplist(壓縮列表):當(dāng)哈希類(lèi)型元素個(gè)數(shù)小于hash-max-ziplist-entries配置(默認(rèn)512個(gè))、同時(shí)所有值都小于hash-max-ziplist-value配置(默認(rèn)64字節(jié))時(shí),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)當(dāng)哈希類(lèi)型無(wú)法滿(mǎn)足ziplist的條件時(shí),Redis會(huì)使用hashtable作為哈希的內(nèi)部實(shí)現(xiàn),因?yàn)榇藭r(shí)ziplist的讀寫(xiě)效率會(huì)下降,而hashtable的讀寫(xiě)時(shí)間復(fù)雜度為O(1);
可以使用object encoding hashkey查看當(dāng)前hash的內(nèi)部編碼是哪種。
四、使用場(chǎng)景
緩存設(shè)計(jì)
hash類(lèi)型也可以用來(lái)作為緩存信息,相比于使用字符串序列化緩存,hash類(lèi)型變得更加直觀,并且在更新操作上會(huì)更加便捷;可以將每個(gè)用戶(hù)的id定義為鍵后綴,多對(duì)field-value對(duì)應(yīng)每個(gè)用戶(hù)的屬性,一種可能的實(shí)現(xiàn)如下:
UserInfo getUserInfo(long id){
//用戶(hù)id作為后綴
userRedisKey = "user:info:"+ id;
//使用hgetall獲取所有用戶(hù)信息映射關(guān)系
userInfoMap = redis.hgetAll(userRedisKey);
UserInfo userInfo;
if(userInfoMap != null){
//將映射關(guān)系轉(zhuǎn)換為UserInfo
userInfo = transferMapToUserInfo(userInfoMap);
}else{
//從MySQL中獲取用戶(hù)信息
userInfo = mysql.get(id);
//將userInfo變?yōu)橛成潢P(guān)系使用hmset保存到Redis中
redis.hmset(userRedisKey,transferUserInfoToMap(userInfo));
//添加過(guò)期時(shí)間
redis.expire(userRedisKey,3600);
}
return userInfo;
}
使用哈希類(lèi)型和關(guān)系型數(shù)據(jù)庫(kù)的不同之處:
- hash類(lèi)型是稀疏的,而關(guān)系數(shù)據(jù)庫(kù)時(shí)完全結(jié)構(gòu)化的,例如hash類(lèi)型每個(gè)鍵都可以有不同的field,而關(guān)系數(shù)據(jù)庫(kù)一旦添加新的列,所有行都要為其設(shè)置值(含null);
- 關(guān)系型數(shù)據(jù)庫(kù)可以作復(fù)雜的關(guān)系查詢(xún),而Redis去模擬關(guān)系型復(fù)雜查詢(xún)開(kāi)發(fā)困難,維護(hù)成本高;
緩存實(shí)現(xiàn)方法優(yōu)劣分析:
1.原生字符串類(lèi)型:每個(gè)屬性一個(gè)鍵;
set user:1:name vechace
set user:1:age 22
set user:1:city shenzhen
優(yōu)點(diǎn):簡(jiǎn)單直觀,每個(gè)屬性都支持更新操作;
缺點(diǎn):占用過(guò)多的鍵,內(nèi)存占用量較大,同時(shí)用戶(hù)信息內(nèi)聚性比較差,一般不會(huì)在生產(chǎn)環(huán)境中直接使用;
2.序列化字符串類(lèi)型:將用戶(hù)信息序列化后用一個(gè)鍵保存;
set user:1 serialize(userInfo)
優(yōu)點(diǎn):簡(jiǎn)化編程,合理使用序列化可以提高內(nèi)存的使用效率;
缺點(diǎn):序列化和反序列化有一定的開(kāi)銷(xiāo),同時(shí)每次更新屬性都需要把全部數(shù)據(jù)取出,進(jìn)行反序列化,更新后再序列化到Redis中;
3.hash類(lèi)型:每個(gè)用戶(hù)屬性使用一對(duì)field-value,但是只用一個(gè)鍵保存;
hmset user:1 name vechace age 22 city shenzhen
優(yōu)點(diǎn):簡(jiǎn)單直觀,合理使用也可以減少內(nèi)存空間的使用;
缺點(diǎn):要控制hash在ziplist和hashtable兩種內(nèi)部編碼的轉(zhuǎn)換,hashtable會(huì)消耗更多的內(nèi)存。
感謝閱讀~
參考資料:
1、《Redis開(kāi)發(fā)與運(yùn)維》