目錄
緩存大致可以分爲兩類,一種是應用內緩存,比如Map(簡單的數據結構),以及EH Cache(Java第三方庫),另一種 就是緩存組件,比如Memached,Redis;Redis(remote dictionary server)是一個基於KEY-VALUE的高性能的 存儲系統,通過提供多種鍵值數據類型來適應不同場景下的緩存與存儲需求
存儲結構
大家一定對字典類型的數據結構非常熟悉,比如map ,通過key-value的方式存儲的結構。 redis的全稱是remote dictionary server(遠程字典服務器),它以字典結構存儲數據,並允許其他應用通過TCP協議讀寫字典中的內容。數據結構如下
數據類型
字符串類型(String)
字符串類型是redis中最基本的數據類型,它能存儲任何形式的字符串,包括二進制數據。你可以用它存儲用戶的 郵箱、json化的對象甚至是圖片。一個字符類型鍵允許存儲的最大容量是512M
內部數據結構
typedef char *sds;
redis3.2分支引入了五種sdshdr類型,目的是爲了滿足不同長度字符串可以使用不同大小的Header,從而節省內存,每次在創建一個sds時根據sds的實際長度判斷應該選擇什麼類型的sdshdr,不同類型的sdshdr佔用的內存空間不同。這樣細分一下可以省去很多不必要的內存開銷,下面是3.2的sdshdr定義
// 8表示字符串最大長度是2^8-1 (長度爲255)
struct __attribute__ ((__packed__)) sdshdr8 {
//表示當前sds的長度(單位是字節)
uint8_t len;
//表示已爲sds分配的內存大小(單位是字節)
uint8_t alloc;
//用一個字節表示當前sdshdr的類型,因爲有sdshdr有五種類型,所以至少需要3位來表示000:sdshdr5,001:sdshdr8,010:sdshdr16,011:sdshdr32,100:sdshdr64。高5位 用不到所以都爲0
unsigned char flags;
//sds實際存放的位置
char buf[];
};
列表類型(list)
列表類型(list)可以存儲一個有序的字符串列表,常用的操作是向列表兩端添加元素或者獲得列表的某一個片段。
列表類型內部使用雙向鏈表實現,所以向列表兩端添加元素的時間複雜度爲O(1), 獲取越接近兩端的元素速度就越快。這意味着即使是一個有幾千萬個元素的列表,獲取頭部或尾部的10條記錄也是很快的
內部數據結構
- 列表對象保存的所有字符串元素的長度都小於64字節
- 列表對象保存的元素數量小於512個
哈希類型(hash)
內部數據結構
- 一種是ziplist,上面已經提到過。當存儲的數據超過配置的閥值時就是轉用hashtable的結構。這種轉換比較消耗性能,所以應該儘量避免這種轉換操作。同時滿足以下兩個條件時纔會使用這種結構:
- 當鍵的個數小於hash-max-ziplist-entries(默認512)
- 當所有值都小於hash-max-ziplist-value(默認64)
- 另一種就是hashtable。這種結構的時間複雜度爲O(1),但是會消耗比較多的內存空間
集合類型(set)
集合類型中,每個元素都是不同的,也就是不能有重複數據,同時集合類型中的數據是無序的。一個集合類型鍵可 以存儲至多232-1個 。集合類型和列表類型的最大的區別是有序性和唯一性 集合類型的常用操作是向集合中加入或刪除元素、判斷某個元素是否存在。由於集合類型在redis內部是使用的值 爲空的散列表(hash table),所以這些操作的時間複雜度都是O(1).
內部數據結構
有序集合(sorted-set)
有序集合類型,顧名思義,和前面講的集合類型的區別就是多了有序的功能
在集合類型的基礎上,有序集合類型爲集合中的每個元素都關聯了一個分數,這使得我們不僅可以完成插入、刪除 和判斷元素是否存在等集合類型支持的操作,還能獲得分數最高(或最低)的前N個元素、獲得指定分數範圍內的元 素等與分數有關的操作。雖然集合中每個元素都是不同的,但是他們的分數卻可以相同
內部數據結構
- 一種是ziplist結構
與上面的hash中的ziplist類似,member和score順序存放並按score的順序排列
- 另一種是skiplist與dict的結合
skiplist是一種跳躍表結構,用於有序集合中快速查找,大多數情況下它的效率與平衡樹差不多,但比平衡樹實現簡單。redis的作者對普通的跳躍表進行了修改,包括添加span\tail\backward指針、score的值可重複這些設計,從而實現排序功能和反向遍歷的功能。
一般跳躍表的實現,主要包含以下幾個部分:
- 表頭(head):指向頭節點
- 表尾(tail):指向尾節點
- 節點(node):實際保存的元素節點,每個節點可以有多層,層數是在創建此節點的時候隨機生成的一個數值,而且每一層都是一個指向後面某個節點的指針。
- 層(level):目前表內節點的最大層數
- 長度(length):節點的數量。
跳躍表的遍歷總是從高層開始,然後隨着元素值範圍的縮小,慢慢降低到低層。
跳躍表的實現原理可以參考:https://blog.csdn.net/Acceptedxukai/article/details/17333673
前面也說了,有序列表是使用skiplist和dict結合實現的,skiplist用來保障有序性和訪問查找性能,dict就用來存儲元素信息,並且dict的訪問時間複雜度爲O(1)。