Redis
簡單來說就是,一個鍵值對類型的單線程內存數據庫。
支持String、List、Set、Sorted Set、hashes數據類型。
數據結構
簡單動態字符串sds
簡單動態字符串(Simple Dynamic String),redis是用C語言寫的,但它的簡單動態字符串卻比C語言的字符串優秀。數據結構如圖:
free:分配了但未使用的內存字節長度
len:已經使用的內存字節長度。不含 '\0'
C語言的字符串結構如圖:
1. 獲取字符串長度複雜的從從 O(N) 變成 O(1)
2. 在操作字符串時可以避免緩衝區溢出
3. 減少了修改字符串引起的內存重分配次數
4. 二進制安全:C語言字符串不能存某些特殊字符串,而簡單動態字符串可以
內存策略:
空間預分配:修改時如果需要擴展,分配空間也分配未使用空間。free = min(len + 1MB)。總長度= len + 1byte + free.
惰性空間釋放:縮短字符串時,不立即釋放空間,而是用free記錄數量
鏈表linkedlist
鏈表是一種常用的數據結構,不過C語言沒有內置。實現也簡單。
維護表頭、表尾指針,表長度及操作表的函數。
字典與哈希表dict
字典由兩個哈希表組成,其中一個正常情況下是空的,也增加了自己的屬性。
內存策略:擴展與收縮,
漸進式rehash
1. 爲 ht[1] 分配空間
2. 修改 rehashidx 爲0,表示開始 rehash
3. 依次把ht[0] 裏的所有鍵值對 rehash到 ht[1],每rehash 一個,rehashidx 加1
4. 期間 刪除、查找、更新在兩個哈希表上進行,添加元素則在新哈希表上進行。則最後舊錶一定爲空
5. 交互 ht[1] ht[0],同時 把 rehashidx 置爲0
跳躍表skiplist
簡單來說,跳躍表是鏈表的一種變形。在鏈表的基礎上添加層level的概念。
待續
整數集合intset
從名字可知,用來存儲整數的
contents 數組類型取決於encoding,而encoding取值有:INTSET_ENC_INT16、INTSET_ENC_INT32、INTSET_ENC_INT64,分別代表int16_t、int32_t、int64_t 類型的值。
升級:當新加元素的類型比現有所有元素的類型都長時,先升級整數集合類型,然後添加元素到數組開頭或者末尾。
注意:因爲新元素引起了升級,因此它肯定比所有元素大(爲正數),放末尾;或者比所有元素小(爲負數),放開頭。
使用升級策略目的:提升整數集合的靈活性,再就是節約內存。
另外,整數集合不支持降級。
壓縮列表ziplist
壓縮列表是redis爲了節約內存而開發的,由一系列特殊編碼的連續內存塊組成的順序數據結構。包含任何多個節點 entry,每個節點保存一個字節數組或者一個整數值。
注意:跟其他數據結構不同,它不是結構體。嚴格來說它不是數據結構,只是有組織的數據存儲方式而已。
連鎖更新:previous_entry_lenght 記錄了前一個節點長度,用1或則會5個字節。增加或者刪除前一個節點時,當前節點記錄的長度發生變化,因此需要擴展空間。依次需要把後邊連續的內存空間移動。複雜度O(N).
redis對象
所謂redis對象,就是一個結構體對象,讓 void *ptr 指向它所使用的上述介紹的各種結構存儲。
用它包裝之前說的數據結構構成redis支持的數據類型:
比如ptr指向簡單動態字符串,構成字符串對象:
再比如哈希對象:
每種數據類型都有兩種數據結構實現,字符串類型有三種。至於redis底層選擇哪種,自行了解。這裏簡單列舉下數據類型和結構組合
對象 |
對應type |
可用編碼 |
|
字符串 |
REDIS_STRING |
REDIS_ENCODING_INT |
利用整數值實現的字符串 |
REDIS_ENCODING_EMBSTR |
利用embstr編碼的sds實現的字符串對象 |
||
REDIS_ENCODING_RAW |
利用簡單動態字符串實現的字符串 |
||
列表 |
REDIS_LIST |
REDIS_ENCODING_ZIPLIST |
使用壓縮列表實現列表對象 |
REDIS_ENCODING_LINKEDLIST |
使用雙端列表實現列表對象 |
||
哈希對象 |
REDIS_HASH |
REDIS_ENCODING_ZIPLIST |
使用壓縮列表實現哈希對象 |
REDIS_ENCODING_HT |
使用字典實現哈希對象 |
||
集合 |
REDIS_SET |
REDIS_ENCODING_INSET |
使用整數集合實現集合對象 |
REDIS_ENCODING_HT |
使用字典實現集合對象 |
||
有序集合 |
REDIS_ZSET |
REDIS_ENCODING_ZIPLIS |
使用壓縮列表實現有序集合對象 |
REDIS_ENCODING_SKIPLIST |
使用跳躍表和字典實現有序集合對象 |
數據庫
redis 服務器就是一個內存數據庫。所有數據都保存在redisServer 結構中,其中又分鍵空間與過期空間兩個字典:
服務器上可以有多個數據庫,一般我們操作的是db[0],如果需要切換數據庫,執行 SELECT x; x是數據庫序號
對於每個數據庫,都有鍵空間與過期空間。顧名思義
鍵空間存儲鍵值對。
過期空間存儲過期時間數據,但兩個空間是共用鍵的。
客戶端
所有客戶端都存在list *clients 中,是一個列表。