读《redis设计与实现》笔记--redis数据结构
redis五大數據結構:string,hash,list,set,zset(有序集合)
redis底層數據結構:簡單動態字符串(SDS),鏈表,字典,跳表,整數集合,壓縮列表
底層數據結構詳解:
1.簡單動態字符串:類似于c的結構體,但是SDS擁有記錄已用長度(len)與剩余空間長度(free),當空間不足時會進行擴容。SDS最后會保存一個空字符
所以SDS獲取字符串長度的時間復雜度為o(1),SDS自動擴容也不會像C一樣產生溢出
利用未使用空間,SDS擁有空間預分配和惰性空間釋放兩種優化方法。
空間預分配:
修改SDS之后之后,如果len<1MB,將free修改為和len一樣的長度,如果len>=1MB,將free修改為1MB。
例如:str = "redis"
如果拼接str與"cluster",那么修改之后len為5+6=11,11+11+1=23,len/free值都是11,加上最后一個空字符,總長為23。
要是str修改之后大小為20MB,那擴容之后就是20MB+1MB+1byte
將擴容需要的次數從最多n次變為至多n次。
惰性空間釋放:
不會真正釋放內存大小,例如刪除redis中的字符e與d,那么str就變為ris,但是free=2。
這樣減少釋放空間的操作,將來擴容也會減少操作。
tips:SDS有專門的api可以真正釋放空間,不用擔心惰性釋放帶來空間的浪費。
二進制安全
C字符串中的字符必須符合某種編碼(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否則最先被程序讀入的空字符將被誤認為是字符串結尾,這些限制使得C字符串只能保存文本數據,而不能保存像圖片、音頻、視頻、壓縮文件這樣的二進制數據。
因此,為了確保Redis可以適用于各種不同的使用場景,SDS的API都是二進制安全的。
SDS和c的區別:
?
2.鏈表
應用:列表鍵的底層實現之一就是鏈表。當一個列表鍵包含了數量比較多的元素,又或者列表中包含的元素都是比較長的字符串時,Redis就會使用鏈表作為列表鍵的底層實現。
除了鏈表鍵之外,發布與訂閱、慢查詢、監視器等功能也用到了鏈表,Redis服務器本身還使用鏈表來保存多個客戶端的狀態信息,以及使用鏈表來構建客戶端輸出緩沖區。
一個鏈表結點:
鏈表:
3.字典(key-value)
字典在Redis中的應用相當廣泛,比如Redis的數據庫就是使用字典來作為底層實現的,對數據庫的增、刪、查、改操作也是構建在對字典的操作之上的。
字典還是哈希鍵的底層實現之一,當一個哈希鍵包含的鍵值對比較多,又或者鍵值對中的元素都是比較長的字符串時,Redis就會使用字典作為哈希鍵的底層實現。
Redis的字典使用哈希表作為底層實現,而每個哈希表節點就保存了字典中的一個鍵值對。
字典所用的哈希表結構:
每個dictEntry結構保存著一個鍵值對
dictEntry結構:
字典結構:
type屬性是一個指向dictType結構的指針,每個dictType結構保存了一簇用于操作特定類型鍵值對的函數,Redis會為用途不同的字典設置不同的類型特定函數。
privdata屬性則保存了需要傳給那些類型特定函數的可選參數。
Redis的哈希表使用鏈地址法來解決鍵沖突。
擴展和收縮哈希表的工作可以通過執行rehash(重新散列)操作來完成。
Redis Bgsave 命令用于在后臺異步保存當前數據庫的數據到磁盤。
對哈希表進行擴展:1.正在執行bgsave,負載因子>=5? ? ? 2.沒有執行bgsave,負載因子>=1
對哈希表進行收縮:負載因子<0.1
漸進式hash:rehash動作并不是一次性、集中式地完成的,而是分多次、漸進式地完成的。
原因:如果哈希表里保存的鍵值對數量千萬甚至億個鍵值對,那么要一次性將這些鍵值對全部rehash的話,龐大的計算量可能會導致服務器在一段時間內停止服務。
所以rehash期間,更新,刪除,查找在兩個表里進行,舊表查找不到的話去新表查找。
而插入就直接在新表進行,所以舊表只減不增。
字典使用哈希表作為底層實現,每個字典帶有兩個哈希表,一個平時使用,另一個僅在進行rehash時使用。
4.跳躍表
Redis使用跳躍表作為有序集合鍵的底層實現之一,如果一個有序集合包含的元素數量比較多,又或者有序集合中元素的成員是比較長的字符串時,Redis就會使用跳躍表來作為有序集合鍵的底層實現。
Redis只在兩個地方用到了跳躍表,一個是實現有序集合鍵,另一個是在集群節點中用作內部數據結構。
每個節點都有向前,向后的指針,向后的可能有多個(看有多少層有這個數據),向前的只有一個。
插入方法:從底層開始,每一層選擇插入或者不插入,插入一層的話就停止。
所以第一層的插入概率1/2,第二次1/4...
5.整數集合
整數集合是集合鍵的底層實現之一。
整數集合的結構:
contents數組各個項在數組中按值的大小從小到大有序地排列,并且數組中不包含任何重復項。contents數組的真正類型取決于encoding屬性的值。
升級:
每當我們要將一個新元素添加到整數集合里面,并且新元素的類型比整數集合現有所有元素的類型都要長時,整數集合需要先進行升級,然后才能將新元素添加到整數集合里面。
升級步驟:1.根據新數據類型,擴展底層數組大小 2.將其他數據設置為新數據類型 3.新元素添加到底層數組
升級好處:一個是提升整數集合的靈活性,另一個是盡可能地節約內存。
節約內存:有int_16,int_32,int_64三種類型,整數集合現在的做法既可以讓集合能同時保存三種不同類型的值,又可以確保升級操作只會在有需要的時候進行,這可以盡量節省內存。
沒有降級操作。
6.壓縮列表
壓縮列表是列表鍵和哈希鍵的底層實現之一。
一個列表鍵只包含少量列表項,并且每個列表項要么就是小整數值,要么就是長度比較短的字符串,就會用壓縮列表。
總結
以上是生活随笔為你收集整理的读《redis设计与实现》笔记--redis数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Leetcode--5. 最长回文子串(
- 下一篇: css3 固定,CSS3 calc()不