這一節我們介紹Hash(字典)數據類型
一、hash(字典)簡單介紹
1、Redis的hash相當於java語言裏面的HashMap,內部存儲了很多鍵值對,實現結構也和HashMap是類似的,都是基於數組+鏈表實現的二維結構。
2、不同的是Redis的字典結構只能存儲字符串。
3、Redis的rehash操作採用了漸進式resha策略,漸進式rehash會在rehash操作時,保留新舊兩個hash結構,查詢時會同時查詢兩個hash結構,在後續的定時任務以及hash指令操作時,漸進地將舊的數據遷移到新的hash結構中。
4、hash結構也可以用來存儲用戶信息,與字符串需要一次性全部序列化整個對象不同,hash可以對用戶結構中每個單獨的字段進行存儲。這樣當我們需要獲取用戶信息時,可以部分獲取,避免浪費網絡流量。
5、hash結構的存儲消耗要高於單個字符串,所以在使用hash存儲還是使用字符串存儲時,要注意權衡。
6、hash結構中單個的key也可以進行計數操作,對應的指令是hincrby,和incr的使用方法基本一致。
com.xiaozhameng.aliyun:6379> hset books java 'think in java' python 'python cookbook'
(integer) 2
com.xiaozhameng.aliyun:6379> hset books golang 'learning go programming'
(integer) 1
com.xiaozhameng.aliyun:6379> hgetall books
1) "java"
2) "think in java"
3) "python"
4) "python cookbook"
5) "golang"
6) "learning go programming"
com.xiaozhameng.aliyun:6379> hlen books
(integer) 3
com.xiaozhameng.aliyun:6379> hget books java
"think in java"
com.xiaozhameng.aliyun:6379> hset user-zhameng age 26
(integer) 1
com.xiaozhameng.aliyun:6379> hincrby user-zhameng age 1
(integer) 27
com.xiaozhameng.aliyun:6379>
二、字典(hash)內部實現
1、字典是Redis中使用較爲頻繁的複合數據結構,除了hash結構會用到字典外,整個redis數據庫的所有key和value也組成了一個全局字典。帶有過期時間的key的集合也是一個字典,zset集合中存儲value和score值的映射關係也是通過字典結構來實現的。
struct RedisDb {
dict* dict; // all keys key => value
dict* expires; // all expired keys key=>long(timestamp)
}
struct zset {
dict *dict; // all values values=>score
zskiplist *zsl;
}
2、字典結構的內部包含了兩個hashtable,通常情況下只有一個hashtable有值,但是在字典擴容縮容時,需要分配新的hashtable,然後進行漸進式搬遷。這時候兩個hashtable存儲的分別是舊的hashtable和新的hashtable,搬遷結束後,舊的hashtable被刪除,新的hashtable取而代之。
3、字典數據類型的hashtable結構和java的HashMap 幾乎是一樣的,都是通過一維數組加上二維鏈表實現的,採用分桶的方式解決hash衝突。
4、漸進式rehash,大字典的擴容是比較耗時的,需要重新申請數組,然後將舊字典中所有鏈表中的元素重新搬遷到新的數組下面,這是一個O(n)時間複雜度的操作,所以redis使用了漸進式rehash,雖然搬遷的過程變長了,但是肯定能搬完。你可能有疑問,既然redis的搬遷操作是在後續的hash操作中發生的,那麼當客戶端閒下來的時候,沒有指令觸發搬遷,redis的搬遷就停止了嗎? 當然不是的,redis設計了定時任務對字典進行主動搬遷。
5、查找過程也是類似java中的查找。這裏不做贅述。
6、擴容條件,正常情況下,當hash表中的元素個數等於一維數組的長度時,就會觸發擴容,擴容新數組的長度是原來容量的二倍。不過如果redis正在做bgsave,爲了減少內存頁的過多分離(Copy On Write),redis儘量不去擴容,但是如果hash表已經非常滿了,元素的個數已經達到了一維數組長度的5倍,這個時候就會強制擴容。
7、縮容,縮容的條件是元素個數低於數組長度的10%,縮容不會考慮redis的bgsave操作。
8、Redis Bgsave 命令用於在後臺異步保存當前數據庫的數據到磁盤的操作。BGSAVE 命令執行之後立即返回 ,然後 Redis fork 出一個新子進程,原來的 Redis 進程(父進程)繼續處理客戶端請求,而子進程則負責將數據保存到磁盤,然後退出。
參考博客:https://www.jianshu.com/p/658365f0abfc
參考書籍:redis深度歷險-核心原理與應用實戰