題記:這本書是2015年11月份開始讀的,大約花了一個多月的時間通讀了一遍,最近因爲須要對redis作一些深刻的瞭解,所以又花了兩個多月仔細精讀了一遍,因爲本書設計的內容較多,且每部分的內容都比較細緻,所以在整理讀書筆記的時候花了不少時間,但確實也收穫了不少,本書是針對redis底層的數據結構部分作的整理。 -----Dimmacro 2016年11月7日18:21:54。java
咱們知道redis能夠存儲字符串、列表、哈希對象、集合、有序集合等五種對象類型,可是在redis內部,根據不一樣對象類型的數據特色,有對應的數據結構做爲其底層的存儲結構,並且即便是同一對象類型,當數據量或數據大小不一樣時,爲了提供更好的性能及減小內存使用率,也會切換不一樣的底層數據結構。所以瞭解這些底層數據結構對咱們深刻了解redis分佈式緩存有很是重要的幫助。redis
(一)數據結構部分-字符串
- redis是鍵值對數據庫,每一個鍵都是字符串對象,而值能夠是字符串對象,列表對象,哈希對象,集合對象,有序集合對象五種之一;
- redis使用sds(simple dynamic string)簡單動態字符串來表示最基本的字符串數據,該結構記錄了用於保存字符串的字節數組char buf[]、已使用長度int len和未使用長度int free。有點相似於java中的String對象。
- 此sds利用c字符串做爲字面量,並遵循以空字符'\0'做爲字符串末尾的C風格,使得其能夠直接重用C字符串函數庫的部分函數,但相比較於C字符串有如下優勢:
- 直接保存字符串長度而不是像C那樣須要遍歷才能獲取長度;
- 經過空間預分配及惰性空間釋放來減小因爲修改字符串帶來的內存重分配。空間預分配是指:當須要擴展字符數組容量時,若是分配後的長度將小於1MB,那麼會預分配與當前len長度同樣的字節量,若是超過1MB,則會分配1MB。惰性空間釋放是指:當縮短sds字符串時,多餘出來的字節數組並不回收,而是經過增加free記錄起來,這樣下次當須要增加到時候若是free自己就夠用了,就不須要申請內存了。固然,也有API可調用來主動釋放。
- 使用二進制方式處理buf數組,保持二進制數據,所以能夠保存除文本數據外的其餘格式,如圖片,音視頻,壓縮文件等;
-
(二)數據結構部分-鏈表linkedlist
- 鏈表經過高效的節點重排、順序訪問、增刪節點靈活調整期長度等特色,應用於redis中的列表鍵、發佈與訂閱、慢查詢、監視器等;
- 鏈表的數據結構:表頭head、表尾tail、節點數量即長度len、節點值複製函數dup、釋放函數free、節點值對比函數match;
- 表中節點數據結構:前置節點prev、後置節點next、節點值value;
- 鏈表特色:雙端、無環、表頭表尾指針、長度計數器、多態(使用void *指針來保存節點值,能夠用於保存各類不一樣類型的值)
(三)數據結構部分-字典hashtable(字典--->哈希表2張--->哈希表數組--->哈希表節點)
- 字典是redis數據庫的底層實現,對數據的增刪查改操做都是構建在字典操做上的;
- 字典dict數據結構:類型特定函數dictType(多態字典)、私有函數void *privdata(類型特定的參數)、哈希表數據dictht ht[2](兩個哈希表用於作rehash和漸進式hast);
- 哈希表是字典的底層實現,其結構爲:dictEntry **table哈希表數組、long size哈希表大小、long sizemask哈希表掩碼,用於計算索引值、long used哈希表已有節點數量、int trehashidx rehash是否在進行的標識;
- 哈希表數組的元素是哈希表節點,是保存字典中鍵值對的地方,即真正保存數據的地方,其結構爲:*key鍵指針、v值、dictEntry *next下個哈希表節點,用於哈希值相同時,將當期值插入到表頭,造成列表,解決鍵衝突問題;
- 哈希過程:根據鍵值使用MurmurHash2算法計算哈希值,而後與表掩碼取模,獲得index做爲存放哈希表數組的位置,若是當前index已經有節點了,則在此節點頭插入當前節點,造成鏈表。相似java中map結構put值的過程;
- rehash的過程:當字典中的哈希表[0]變化的時候,爲了讓負載因子維持在合理範圍,會作rehash操做,其步驟以下:
- 爲哈希表[1]分配空間,並根據擴展仍是壓縮操做設置其大小,值爲2的N次方,擴展值爲第一個大於等於ht[0]*used*2的2的n次冪,收縮操做爲第一個大於等於ht[0]*used的2的N次冪;
- 將ht[0]中的全部鍵值rehash到ht[1]上,完成後釋放ht[0],並間ht[1]設爲ht[0],並從新建立一個空白的ht[1哈希表爲下一次rehash作準備;(若是鍵值量大,會採用漸進式rehash的方式,在此期間會同時使用ht[0]和ht[1])。
(四)數據結構部分-跳躍表skiplist
- 跳躍表是一種有序數據結構,經過在每一個節點中維持多個指向其餘及節點的指針達到快速訪問節點的目的;
- redis使用跳躍表用做實現有序集合鍵以及在集羣節點中用做內部數據結構
(五)數據結構部分-整數集合intset
- 用於少許整數的集合,是集合鍵的底層實現之一;
- 整數集合intset由encoding編碼方式、length包含元素數量、contents元素數組三部分組成,各項在數組contents中按值的大小從小到大有序排列,不包含重複項;
- 當新添加的元素值大於現有集合encoding制定的範圍時引起升級,現有元素所在位數增長;升級操做能夠提高存儲數據的靈活性,並節約內存;
- 不支持降級操做
(六)數據結構部分-壓縮列表ziplist
- 壓縮列表是一系列特殊編碼的連續內存塊組成的順序型數據結構,爲節約內存而開發,是列表鍵和哈希鍵的底層實現之一;
- 組成部分:zlbytes:4個字節,記錄整個壓縮列表佔用的內存字節數;zltail4個字節,記錄表尾節點距離壓縮列表起始地址有多少字節;zllen,2個字節,記錄壓縮列表包含的節點數量;entryX列表節點;zlend:1個字節,特殊值0xFF用於標記壓縮列表的末端;
- 列表節點由三部分構成:previous_entry_length記錄壓縮列表中前一個節點的長度,可用於從表尾向表頭遍歷;encoding記錄本節點所保存的數據類型及長度;content保存節點值,能夠是字節數組或整數;
- 當前一節點長度小於254字節,previous_entry_length佔1字節,不然佔5字節,所以有可能插入一個長度大於254字節的節點到表頭是,若是後續節點都介於250到253之間,則可能發生連鎖更新,影響性能。不過幾率很小。
######以上文字來自Dimmacro,轉載請說明來源:http://www.cnblogs.com/dimmacro/ #######算法