Redis基础数据结构内部实现简单介绍
生活随笔
收集整理的這篇文章主要介紹了
Redis基础数据结构内部实现简单介绍
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
5種基礎數據結構
- Redis有5種基礎數據結構,分別是:String(字符串),list(列表),hash(字典),set(集合),zset(有序集合),這五種是我們開發種經常用的到的,是Redis種最基礎,最重要的部分。
string(字符串)
- 字符串string是Redis最簡單的一個數據結構,他的內部表示是一個字符數組,Redis所有的數據結構都以唯一的key字符串作為名稱,然后通過這個唯一的key獲取對應的value數據,不同類型數據結構差異其實就在value上而已。如下圖對應String類型字符串示意圖:
- Redis的字符串底層實現其實是動態的字符串,是可以修改的字符串,內部結構實現類似java種的ArrayList,采用預分配的方式冗余了一部分空的內存空間來減少頻繁的內存重新分配的環節,如下圖,內部為當前字符串分配的實際空間Capacity,一般比我們字符串長度len更多,當字符串長度小于1M時候,擴容都是加倍現有空間,如果字符串長度超過1M,擴容的時候,只會擴容1M內存(生產上一個1M的字符也比較大了,不建議這么搞)。注意,字符串最大長度512M。
- 如果這個string類型的數據value是一個整數,Redis還提供來一種自增的操作,他的自增范圍是signed long的最大值最小值之間,超過這個范圍,Redis會報錯。
list(列表)
- Redis的list相當于Java中的LinkedList,他是鏈表而不是數組。這個意味著list的插入和刪除是很快的,事件復雜度是O(1),但是下標索引定位慢,事件復雜度O(n),Redis中實現是一個雙向鏈表結構的保證順序性,串起來可以同時支持前后的方向便利,當列表彈出最后一個元素,該數據結構被自動刪除,內存回收。如下圖
- Redis的列表結構可以用來做簡單的異步隊列使用,將需要延后處理的任務結構化為一個字符串,放到Redis列表中,另一個線程在列表中逐個彈出。
右近左出:隊列
- 隊列是先進先出的數據結構,用于消費隊列和異步的邏輯處理,他會確保元素的訪問順序性。如下圖
右邊進右邊出:棧
- 棧是先進后出的數據結構,跟隊列正好相反,我們用Redis的列表數據結構來做棧使用如下:
警惕慢操作
- lindex相當于java鏈表的get方法,他需要遍歷整個鏈表,性能O(n),隨著參數index的增加而增加
- ltrim 與字面上意思不一致,保留一段期間內的值,刪掉區間以外的,可以通過這個來獲取一個定長列表,index可以是負數,-1是倒數第一個,-2 倒數第二個
內部存儲:快速列表
- Redis的底層存儲的不是一個簡單的LinkedList,而是一個快速列表的一個結構
- 首先元素較少的時候,會使用一塊連續的內存存儲,這個結構叫做ziplist,壓縮列表。他所有的元素彼此緊挨在一起存儲,分配的是一塊連續的內存(可以看作一個數組)
- 當數據量比較多的時候才會改成quicklist。因為普通列表需要的附加指針占用空間太大比如我們list中都是int類型的數據,那么每一個節點還需要維護兩個指針prev,next,指針所占用的內存比實際數據還要多
- 所以Redis將鏈表和ziplist結合使用,組成quicklist,也就是將多個ziplist用雙向指針串起來,就可以節省很多指針空間。如下圖。
hash(字典)
- Redis的字典相當于Java中的HashMap,,他是無順序的字典結構,內存存儲多個key,value,
- 實現結構與Java中HashMap也類似,都是數組+鏈表的而為結構,如下圖,第一緯度的hash結構數組會發生重合的情況,也就是兩個key的hash值一樣,就會使用鏈表的形式將元素串起來。
- 不同地方Redis的字典值只能是字符串,另外他們的rehash的方式也不一樣,因為Java的HashMap字典很大時候,rehash的耗時很多,而且一次性全部完成,Redis為追求高性能,不能堵塞服務,所以采用漸進式rehash策略
- 漸進式rehash會在rehash的同時,保留新舊兩個hash結構,查詢時候同時查詢兩個字典結構,后續Redis會在定時任務以及hash操作指令中,逐漸的將舊的hash內容一點點遷移到新的hash結構,完成后才用新的。(空間換時間)
- 當hash結構最后一個元素被移除,該數據結構會自動刪掉,內存自動回收。
- 同字符串一樣,hash結構也有單個key的計數功能,他對于指令是hincrby,和incr使用方法基本一致
set(集合)
- Redis的集合相當于java中的HashSet,內部的鍵值對是無序的,唯一的。他的內部實現相當于一個特殊的字典,字典中的所有value都是一個NULL而已,同樣當集合最后一個元素被移除也會被刪除,自動回收內存。
- set結構可以用來存儲中獎用戶id,因為有去重的功能,保證唯一性。
zset(有序列表)
- zset是Redis提供的最有特色的一個數據結構,類似java的sortedSet和HashMap的結合體,應為他有兩者特點,首先他是一個set,所以保證內部每個value的唯一性,另外他給每個value的排序賦予一個權重score,代表這個value的排序權重,他內部實現是一個叫”跳躍表“的數據結構。
跳躍列表
-
zset的內部排序功能是通過跳躍表數據結構來實現的,他的結構非常特殊,因為zset既要支持隨機的插入和刪除,所以他不能用數組,數組需要遍歷
-
Zset要支持按照score排序,這意味著有新元素插入,需要定位特定的位置插入點,這樣才可以保證有序性,通常我們用二分查找(平均時間復雜度最小),但是二分查找對象都是數組,鏈表無法做到這樣就兩個條件矛盾來用如下的結構來解決這個問題:變異形態的鏈表
-
比如我們平時公司的結構中,研發人員平等,都有一個組長,各個組長平等,組長上有leader,各個leader之間平等,leader上沒有總監,依次向上到最后的CEO,跳躍表就類似這樣的一個層級結構
-
最下面的一層是所有元素都串起來,然后每隔幾個元素挑選出一個代表,再將這幾個代表用另外一個指針串聯。接著在這些代表中繼續挑選出二級代表,形成新的金字塔結構。如下:
-
解釋:
- 類比二分法,當我們用二分發時候,先找到middle位置,比較中間位置的value和將要insert進去元素的value,如果小,則將0~middle作為新的鏈表,如果大middle-max作為新的鏈表重新以上步驟,來到第二層。
- 如上圖,我們定位插入點時候,先從頂層開始,我們將最頂層的一個節點就相當于中間位置,只不過他是我們通過特殊的數據結構暴露出來的一個中間位置,然后類比上面步驟,我們進入到第二層中,比較中間位置的L2和將要insert進去元素的value,如果小,將L1層級中中0~L2位置元素作為新的鏈表,我們通過這樣逐層的比較,直到最底層找到合適的位置,將新的元素insert進去。
- 這種做法就相當于將二分法的步驟拆開后,得到N個鏈表,每個鏈表都是我們二分法中每個步驟中需要查找的那個半個數組而已,通過比較每個數組中的中間value值來得出最后的位置。用冗余的方式來避免鏈表的遍歷(替換數組的下標索引功能)
-
跳躍表中節點可以在多個層級有多個身份,跳躍表采用隨機策略決定新元素可以到第幾層,L0層概率是1 ,L1 只有50%對半分,L2 再次25%,L3只有12.5%,依次,一直隨機到頂層L31,每次對半,到頂層基本不會有很多數據。
容器類型數據結構的通用規則
- list,set, hash, zset四種數據結構是容器型數據結構,他們共享下面幾個規則:
- create if not exists:如果容器不存在,那就創建一個,子啊進行操作,比如rpush操作剛開始沒有列表,會新建一個列表,然后在rpush元素
- drop if no elements:如果容器中沒有元素,會立即刪除容器,釋放內存。意味著lpop操作完最后一個元素后列表就會消失。
- 過期時間設置,到期自動刪除,注意如果一個key設置來過期時間后調用set方法修改,過期時間會消失。
上一篇Redis數據結構以及對應存儲策略
下一篇Redis基礎數據結構之二
總結
以上是生活随笔為你收集整理的Redis基础数据结构内部实现简单介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 刮痧板厚的好还是薄的好
- 下一篇: Redis高级数据结构原理解析-bitm