經常使用命令:html
set,get,decr(減一),incr(加一),mget(返回全部(一個或多個)給定 key 的值) 等。redis
應用場景:數組
String 是最經常使用的一種數據類型,普通的 key/value 存儲均可以歸爲此類,這裏就不所作解釋了。數據結構
實現方式:併發
String 在 redis 內部存儲默認就是一個字符串,被 redisObject 所引用,當遇到 incr,decr 等操做時會轉成數值型進行計算,此時 redisObject 的 encoding 字段爲int。性能
經常使用命令:優化
hget,hset,hgetall 等。線程
應用場景:指針
咱們簡單舉個實例來描述下 Hash 的應用場景,好比咱們要存儲一個用戶信息對象數據,包含如下信息:code
用戶 ID 爲查找的 key,存儲的 value 用戶對象包含姓名,年齡,生日等信息,若是用普通的 key/value 結構來存儲,主要有如下2種存儲方式:
第一種方式將用戶 ID 做爲查找 key,把其餘信息封裝成一個對象以序列化的方式存儲,這種方式的缺點是,增長了序列化/反序列化的開銷,而且在須要修改其中一項信息時,須要把整個對象取回,而且修改操做須要對併發進行保護,引入CAS等複雜問題。
第二種方法是這個用戶信息對象有多少成員就存成多少個 key-value 對兒,用用戶 ID +對應屬性的名稱做爲惟一標識來取得對應屬性的值,雖然省去了序列化開銷和併發問題,可是用戶 ID 爲重複存儲,若是存在大量這樣的數據,內存浪費仍是很是可觀的。
那麼 Redis 提供的 Hash 很好的解決了這個問題,Redis 的 Hash 實際是內部存儲的 Value 爲一個 HashMap,並提供了直接存取這個 Map 成員的接口,以下圖:
也就是說,Key 仍然是用戶 ID,value 是一個 Map,這個 Map 的 key 是成員的屬性名,value 是屬性值,這樣對數據的修改和存取均可以直接經過其內部 Map 的 Key(Redis 裏稱內部 Map 的 key 爲 field),也就是經過 key(用戶 ID) + field(屬性標籤)就能夠操做對應屬性數據了,既不須要重複存儲數據,也不會帶來序列化和併發修改控制的問題。很好的解決了問題。
這裏同時須要注意,Redis 提供了接口(hgetall)能夠直接取到所有的屬性數據,可是若是內部 Map 的成員不少,那麼涉及到遍歷整個內部 Map 的操做,因爲 Redis 單線程模型的緣故,這個遍歷操做可能會比較耗時,而另其它客戶端的請求徹底不響應,這點須要格外注意。
實現方式:
上面已經說到 Redis Hash 對應 Value 內部實際就是一個 HashMap,實際這裏會有2種不一樣實現,這個 Hash 的成員比較少時 Redis 爲了節省內存會採用相似一維數組的方式來緊湊存儲,而不會採用真正的 HashMap 結構,對應的 value redisObject 的 encoding 爲 zipmap,當成員數量增大時會自動轉成真正的 HashMap,此時 encoding 爲 ht。
經常使用命令:
lpush,rpush,lpop,rpop,lrange等。
應用場景:
Redis list 的應用場景很是多,也是 Redis 最重要的數據結構之一,好比 twitter 的關注列表,粉絲列表等均可以用 Redis 的 list 結構來實現,比較好理解,這裏再也不重複。
實現方式:
Redis list 的實現爲一個雙向鏈表,便可以支持反向查找和遍歷,更方便操做,不過帶來了部分額外的內存開銷,Redis 內部的不少實現,包括髮送緩衝隊列等也都是用的這個數據結構。
經常使用命令:
sadd,spop,smembers,sunion 等。
應用場景:
Redis set 對外提供的功能與 list 相似是一個列表的功能,特殊之處在於 set 是能夠自動排重的,當你須要存儲一個列表數據,又不但願出現重複數據時,set 是一個很好的選擇,而且 set 提供了判斷某個成員是否在一個 set 集合內的重要接口,這個也是 list 所不能提供的。
實現方式:
set 的內部實現是一個 value 永遠爲 null 的 HashMap,實際就是經過計算 hash 的方式來快速排重的,這也是 set 能提供判斷一個成員是否在集合內的緣由。
經常使用命令:
zadd,zrange,zrem,zcard等
使用場景:
Redis sorted set 的使用場景與 set 相似,區別是 set 不是自動有序的,而 sorted set 能夠經過用戶額外提供一個優先級(score)的參數來爲成員排序,而且是插入有序的,即自動排序。當你須要一個有序的而且不重複的集合列表,那麼能夠選擇 sorted set 數據結構,好比 twitter 的 public timeline 能夠以發表時間做爲 score 來存儲,這樣獲取時就是自動按時間排好序的。
實現方式:
Redis sorted set 的內部使用 HashMap 和跳躍表(SkipList)來保證數據的存儲和有序,HashMap 裏放的是成員到 score 的映射,而跳躍表裏存放的是全部的成員,排序依據是 HashMap 裏存的 score,使用跳躍表的結構能夠得到比較高的查找效率,而且在實現上比較簡單。
經過咱們上面的一些實現上的分析能夠看出 redis 實際上的內存管理成本很是高,即佔用了過多的內存,做者對這點也很是清楚,因此提供了一系列的參數和手段來控制和節省內存,咱們分別來討論下。
首先最重要的一點是不要開啓 Redis 的 VM 選項,即虛擬內存功能,這個原本是做爲 Redis 存儲超出物理內存數據的一種數據在內存與磁盤換入換出的一個持久化策略,可是其內存管理成本也很是的高,而且咱們後續會分析此種持久化策略並不成熟,因此要關閉 VM 功能,請檢查你的 redis.conf 文件中 vm-enabled 爲 no。
其次最好設置下 redis.conf 中的 maxmemory 選項,該選項是告訴 Redis 當使用了多少物理內存後就開始拒絕後續的寫入請求,該參數能很好的保護好你的 Redis 不會由於使用了過多的物理內存而致使 swap,最終嚴重影響性能甚至崩潰。
另外 Redis 爲不一樣數據類型分別提供了一組參數來控制內存使用,咱們在前面詳細分析過 Redis Hash 是 value 內部爲一個 HashMap,若是該 Map 的成員數比較少,則會採用相似一維線性的緊湊格式來存儲該 Map,即省去了大量指針的內存開銷,這個參數控制對應在 redis.conf 配置文件中下面2項:
hash-max-zipmap-entries 64 hash-max-zipmap-value 512 hash-max-zipmap-entries
含義是當 value 這個 Map 內部不超過多少個成員時會採用線性緊湊格式存儲,默認是64,即 value 內部有64個如下的成員就是使用線性緊湊存儲,超過該值自動轉成真正的 HashMap。
hash-max-zipmap-value 含義是當 value 這個 Map 內部的每一個成員值長度不超過多少字節就會採用線性緊湊存儲來節省空間。
以上2個條件任意一個條件超過設置值都會轉換成真正的 HashMap,也就不會再節省內存了,那麼這個值是否是設置的越大越好呢,答案固然是否認的,HashMap 的優點就是查找和操做的時間複雜度都是 O(1) 的,而放棄 Hash 採用一維存儲則是 O(n) 的時間複雜度,若是成員數量不多,則影響不大,不然會嚴重影響性能,因此要權衡好這個值的設置,整體上仍是最根本的時間成本和空間成本上的權衡。
一樣相似的參數還有: 1 list-max-ziplist-entries 512 說明:list 數據類型多少節點如下會採用去指針的緊湊存儲格式。 1 list-max-ziplist-value 64 說明:list 數據類型節點值大小小於多少字節會採用緊湊存儲格式。 1 set-max-intset-entries 512 說明:set 數據類型內部數據若是所有是數值型,且包含多少節點如下會採用緊湊格式存儲。