Redis 基础——五大类型与数据结构
引言
Redis 區分于 memcahced 的一個重要不同就是它具有明確的類型概念,在Redis 的使用過程中,都離不開這些類型的學習,它不僅是 Redis 能力的基礎,同時也是一些重要數據結構和算法思想的體現。
本博客總結了五大類型的書面重點,幫助快速梳理和總結 Redis 類型相關的知識點,理論性和記憶性較強??梢宰鳛?Redis 數據類型的學習大綱,建議在實踐之前牢記這些知識。
一、Redis簡介
在開始之前,回顧一下redis的介紹性知識。
redis的底層語言是C,它是一種高性能鍵值對、NoSQL內存數據庫。可以用作緩存、數據庫、消息中間件、分布式鎖等。
它的幾大優點:
1、性能優秀:內存中運行,讀寫速度快,支持10W并發QPS。
2、單進程單線程:線程安全,采用IO多路復用機制。
3、豐富的數據結構:五大數據類型,類型檢查和命令多態。
4、數據持久化:AOF和RDB,以及混用模式。
5、高可用方案:主從復制、哨兵模式等。
6、適合多種分布式業務場景:消息中間件、分布式鎖、消息訂閱發布等等。
二、鍵值存儲對象 ——?RedisObject
Redis 中使用 redisObject 對象來表示數據庫中的 key(始終是字符串對象)和 value(五種對象類型的任意一種)。
每次在 Redis 中新創建一個鍵值對時,它至少會創建兩個對象(鍵對象、值對象)。
redisObject 包含幾個重要的屬性:
type?屬性記錄對象類型——五大類型;
encoding 屬性記錄對象所使用的編碼,即對象的底層實現是怎樣的數據結構;
ptr 屬性指向對象的底層數據結構,這些數據結構由encoding決定;
refcount 屬性記錄對象的引用計數,redis 可以改變這個值是否 = 0 來決定是否回收對象內存;同時,它也是實現對象共享機制的基礎。
lru 屬性記錄對象最后一次被命令訪問的時間。
三、五大基本類型
一般我們說Redis中的數據類型通常指值對象的類型,因為鍵始終是字符串對象。
對象類型由 redisObject 中的 type 屬性記錄,它有以下五種類型,可以使用 TYPE 命令查看對象類型:
TYPE keyname五大基本類型:
1、String :最基本的類型,二進制安全可以存儲包括圖片等文件格式,編碼方式有raw、int 等。最大能夠存儲512M。
2、Hash :鍵值對集合對象,類似Java 中的 Map,但鍵值都得是字符串。適合存儲對象信息,且支持更改某一項屬性。
3、List :簡單的字符串列表,底層實現是雙向鏈表。按照插入順序排序,操作分為左右,如 LPUSH、RPUSH等??梢杂米飨㈥犃心P汀?/p>
4、Set :無序、不可重復字符串集合。通過 hashtable實現。增刪查都是O(1)復雜度,支持交、并、差集操作。
5、ZSet:有序、不可重復字符串集合。通過 hashtable 和 skiplist 實現??梢愿鶕?Double 類型的 score 從小到大排序。
三、八種編碼方式
對象編碼表示 Redis 以何種結構結構存儲數據,由 redisObject 的 encoding 屬性記錄。
編碼方式并不一定表示某種具體的數據結構,例如 skiplist 編碼的 ZSet 對象,底層實際使用了字典+跳躍表的復合結構。
Redis 為何要將數據類型拆分為 type 和 encoding 呢?
對象類型不關聯固定的編碼,是為了提升redis的靈活性和效率,同時也有性能方面的考慮。
Redis可以根據不同的使用場景為一個對象設置不同的編碼,從而優化對象在某一場景下的效率。例如,在列表對象包含元素較少時,使用 ziplist,它比 linkedlist 更節約內存,在內存中以連續的方式保存數據,可以更快的載入到緩存中。但如果 List 中的元素越來越多,就會使用 linkedlist,它更適合保存大量元素的場景。
編碼方式有 8 種:
int、embstr、raw、hashtable、linkedlist、ziplist、intset、skiplist
可以使用 OBJECT ENCODING k1 來查看 key 的值對象的編碼方式。
它們與類型的關系如下:
String :int、embstr、raw
Hash :ziplist、hashtable
List :ziplist、linkedlist
Set :intset、hashtable
ZSet? :ziplist、skiplist
四、字符串
39個字節區分 raw 和 embstr (<=39)編碼,embstr 編碼是專門用于保存短字符串的一種優化編碼方式。
對于某些浮點數字符串,在執行類似 INCRBYFLOAT命令時,會先將類型轉化為浮點數,執行運算操作后,再轉換回字符串。
編碼轉換 :int 和 embstr 在某些條件下會轉為 raw
- int :在執行某些命令后,使得值不再是一個整數(如APPEND),那么編碼會從int轉為raw。
-
embstr:它實際上是只讀的,當對embstr執行修改時,Redis 一定會將其轉為 raw,再執行修改命令。
五、哈希
Hash 對象的編碼可以是 ziplist 或 hashtable。
ziplist:是一種連續的數據結構,類似數組,當以 ziplist 存儲鍵值對時,它們會以 k-v-k-v... 的形式間隔存入,因此同一鍵值對的key和value總是緊挨著的。
hashtable:意為“字典”,它以數組保存所有鍵值對,每對鍵值都被封裝為一個叫 entry 的結構,這與Java中的HashMap非常類似。
編碼轉換:當所有鍵和值的字符串長度小于64字節,且鍵值對數量小于512個時,使用ziplist編碼;否則,使用hashtable編碼。當然,這兩個條件的上限是可以修改的。
六、列表
List 對象的編碼可以是 ziplist 或 linkedlist。
ziplist:同 hash類型。
linkedlist:是一種雙端鏈表結構,每個鏈表節點都會包含一個字符串對象,這是一種嵌套字符串行為,字符串對象是Redis五種類型中唯一一種會被其他四種類型對象嵌套的對象。
也就是說,五種類型對象的鍵和值都只能是字符串相關的數據結構。
?編碼轉換:當所有元素長度都小于64字節,且元素個數小于512個時,使用ziplist;否則,使用 linkedlist。限制條件與hash對象是相同的。
七、集合
Set 對象的編碼可以是 intset 或 hashtable。
intset:代表一個整數集合。
hashtable:在實現set時,字典的每個鍵保存了一個元素,而字典的值全部都為NULL。在 Java 中,也會使用 HashMap 來實現 HashSet,不過,在Java中,為了避免空指針,每個 Entry 的值并不是 null,而是都指向了同一個空的 Object。
?編碼轉換:當所有元素都是整數,且元素個數不超過512個時,使用 intset;否則,使用 hashtable。
八、有序集合
ZSet 對象的編碼可以是 ziplist 或 skiplist。
ziplist:每個集合元素使用兩個緊挨在一起的節點來保存,前節點保存元素的成員,后節點保存分數score。
skiplist:是一個復合結構——字典 + 跳躍表。它們會引用共享的數據,不會造成重復存儲的情況。跳躍表可以按分數順序存儲所有元素,程序可以基于此對有序集合進行范圍操作:ZRANK、ZRANGE等;
字典創建了從成員到分數的映射,程序可以用O(1)查找給定成員的分數,如:ZSCORE等。
為什么ZSet 要同時使用跳躍表和字典來實現呢?
單獨使用其中一種都達不到同時使用兩種結構的性能。可以說這兩種結構的結合彌補了有序結構在查找與范圍搜索上的先天不足。因此,為了讓有序集合在查找和范圍操作都盡可能快,Redis 選擇了同時使用字典和跳躍表兩種數據結構來實現ZSet。
編碼的轉換:當元素個數小于128,所有元素長度都小于64字節時,使用ziplist;否則使用skiplist編碼。?
九、Redis 的類型檢查和命令多態
Redis中用于操作key的命令可以分為兩類:對全部類型都可用和只對特定類型可用。
全部類型可用:DEL、EXPIRE、RENAME、TYPE、OBJECT等。
特定類型可用:SET、GET、APPEND、HDEL、RPUSH、SADD、ZADD等等。
9.1 類型檢查的實現
為了保證只有特定類型的key可以執行某些特定命令,在執行特定命令之前,redis會先檢查key 所對應的value的類型,然后決定是否執行。這種類型檢查是通過redisObject的type屬性來實現的。
9.2 多態命令的實現
redis會根據值對象的編碼,選擇正確的“命令實現代碼”來執行命令。
例如,List 的編碼有 ziplist 和 linkedlist 兩種,Redis 會根據encoding的不同,在執行LLEN命令時,考慮使用ziplistlen函數還是使用listlength函數。
以面向對象的術語來說,LLEN命令是多態的。
十、內存回收與對象共享
Redis 使用引用計數來實現對象內存空間的回收。
每個值對象上都有一個引用計數——refcount,Redis 可以通過增加或減少引用計數來實現內存回收和對象共享。
十一、對象的空轉時長
除了type、encoding、ptr、refcount等屬性外,redisObject還有一個屬性lru。
lru屬性記錄了對象最后一次被命令訪問的時間。
OBJECT IDLETIME k1 // 該命令可以查看鍵的空轉時長,這是通過將當前時間減去值對象的lru時間計算得出的。
當redis設置了maxmemory選項,且內存回收算法設置為volatile-lru或allkeys-lru,那么當內存超過maxmemory時,空轉時間較高的那部分key會優先被服務器釋放。
總結
以上是生活随笔為你收集整理的Redis 基础——五大类型与数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php合成图片设置图片大小,php 上传
- 下一篇: mysql5.7主从全备恢复_Mysql