一、關於 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的效能,避免頻繁的記憶體回收開銷。