關於redis,你需要了解的幾點!
一、關於 redis key:
1、是二進制安全的,也就是說,你可以使用任何形式的二進制序列來作為key,比如一個string,或者一個jpg圖片的數據,需要說明的是,空字符串也是一個有效的key。
2、不建議使用過長的key,影響內存佔用及數據查性能,對於過長的key,可以通過hash(例如SHA1)處理轉換。
3、建議使用有意義及統一格式的key。
4、最大允許key大小為512M。
二、String 類型應用:
1、作為原子計數器:incr、decr、incrby
2、結合append命令,作為基於時間的增量序列。
3、隨機訪問及獲取值區域,getrange、setrange。
附:需要注意的是append及range操作容易引起內存浪費和碎片化問題。
三、hash 類型:ziplist or hashtable
1、單個hash最多支持232 – 1個鍵值對。
2、關於hash類型的內部編碼:ziplist(壓縮列表) & hashtable(哈希表)
配置:hash-max-ziplist-entries(hash類型最大kv數據,默認512)、hash-max-ziplist-value(單個v值最大值, 默認64)
redis 採用何種結構取決於hash中元素數及元素值得大小,當同時滿足小於配置時,redis使用ziplist編碼存儲,否則會轉化為hashtable。
ziplist編碼使用更加緊湊的結構實現多個元素的連續存儲,因此佔用的內存更小。
當數據類型無法滿足配置條件,此時使用ziplist編碼存儲讀寫效率會下降,所以轉換使用hashtable編碼存儲(O(1)時間複雜度)。
示例:添加 testuser hash類型key,先後設置元素name、desc不同長度元素值,分別查看內部編碼類型
四、關於redis存儲:redisObject
redis 對象內部存儲形態。
1、type:數據類型、例如sting、hash、list、set、zset等,值類型查看命令【type】。
2、encoding:值存儲內部實現的數據結構,具體可以參考第七部分。
3、lru:最後一次被訪問時間,輔助回收,可以通過 object idletime {key} 在不更新lru屬性情況下查看key的空閑時間。
4、refcount:當前對象被引用次數,輔助回收,可以通過 object refcount {key} 查看引用數,當對象為整數且值在範圍在[0-9999]時,redis可以通過共享對象的方式來節省內存。
目前共享對象池只對整數設置了0~9999個共享對象,一方面整數對象池復用率最大,同時等值判斷上時間複雜度為O(1)。
5、*ptr:數據存儲或指向,數據本身或者指向數據的指針,redis3.0之後,長度在39以內的字符串數據,內部編碼為embstr,內存創建時,字符串和redisObject一起分配,減少一次內存分配。
五、關於SDS
simple dynamic string:redis內部自定義簡單動態字符串結構。
1、字符串屬性的O(1)時間複雜度獲取。
2、空間與分配、減少內存再分配。
3、惰性刪除機制,字符串縮減後空間不釋放,作為預分配空間保留。
六、關於對象屬性存儲:json or hash
對象屬性存儲可以通過整體json存儲或者hash kv存儲。具體應用選擇,可以結合整體對象大小及屬性操作需求來決定。
對於頻繁整體操作,且對象數據量較小的一般採用json字符串類型存儲。
對於多對象屬性層級操作情景,可能hash會比較合適。
七、關於存儲編碼
如上圖,同一種數據類型,可以有多種不同內部編碼存儲形式。具體redis採用那種編碼形式與實際應用的數據值類型相關,如上述第三部分論述hash類型的編碼轉換。
數據的編碼類型在數據寫入的時候確定,不可變換,且只能向大內存編碼行使轉換。
如下,重新設置 testuser desc值,testuser對象的編碼形式保持不變:
編碼轉換時機:
八、關於ziplist
通過第七節,我們可以看到hash、list、zset底層都有應用這種存儲結構。
基本特點:
1、連續性內存存儲。
2、可以模擬雙向鏈表,O(1)時間複雜度內出入隊操作。
3、讀寫性能跟數據的元素個數及值長度相關,適合存儲小對象和長度有限的數據。
4、數據增刪涉及複雜的內存操作。
ziplist基本結構:
1、zlbytes:int32類型、4位元組,ziplist整體位元組長度。
2、zltail:int32類型、4位元組,記錄距離尾節點偏移量,用於尾節點彈出。
3、zllen:int16類型,2位元組,ziplist節點數量。
4、entry:數據節點,長度不定。
entry 即鏈表node,內部結構包含:
prev_entry_bytes_length:前一個節點所佔用的空間,用戶快速定位。
encoding:當前數據節點編碼類型及長度,前兩位標識編碼類型,其餘標識數據長度。
contents:節點數據值。
5、zlend:1位元組,記錄列表結尾。
九、關於redis內存消耗
redis內存消耗包括自身內存,鍵值對象佔用、緩衝區內存佔用及內存碎片佔用。
1、緩衝區內存:包括客戶端緩存、複製及壓緩衝區及AOF緩衝區。
2、內存碎片:固定範圍內存塊兒分配。
redis默認使用jemalloc內存分配器,其它包括glibc、tcmalloc。
內存分配器會首先將可管理的內存分配為規定不同大小的內存塊以備不同的數據存儲需求,但是,我們知道實際應用中需要存儲的數據大小不一,規範不一,內存分配器只能選擇最接近數據需求大小的內存塊兒進行分配,這樣就伴隨着「占不滿」空間的碎片浪費。
jemalloc針對內存碎片有相應的優化策略,正常碎片率為mem_fragmentation_ratio在1.03左右。
第二部分我們說過,對string值得頻繁append及range操作會會導致內存碎片問題,另外,第七部分,SDS惰性內存回收也會導致內存碎片,同時過期鍵內存回收也伴隨着所釋放空間的無法充分利用,導致內存碎片率上升的問題。
碎片處理:
應用層面:盡量避免差異化的鍵值使用,做好數據對齊。
redis服務層面:可以通過重啟服務,進行碎片整理。
3、maxmemory及maxmemory-policy控制redis最大可用內存及內存回收。需要注意的是內存回收執行影響redis的性能,避免頻繁的內存回收開銷。