2.3、哈希

哈希

  • 幾乎所有的編程語言都提供了哈希(hash)類型,它們的叫法可能是哈希、字
    典、關(guān)聯(lián)數(shù)組。在Redis中,哈希類型是指鍵值本身又是一個(gè)鍵值對(duì)結(jié)構(gòu),形如
    value={{field,value},...{fieldN,valueN}}。
  1. 命令

    (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 key

    hkeys命令應(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 field

    hincrby和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)
  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"
    
  2. 使用場(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)存。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容