Redis 已是你們耳熟能詳的東西了,平常工做也都在使用,面試中也是高頻的會涉及到,那麼咱們對它究竟瞭解有多深入呢?java
我讀了幾本 Redis 相關的書籍,嘗試去了解它的具體實現,將一些底層的數據結構及實現原理記錄下來。面試
本文將介紹 Redis 中底層的 skiplist(跳躍表) 的實現方法。 它是 Redis 中有序集合鍵底層實現之一。編程
能夠看到圖中,當我在zsetkey
中放入了兩個簡單的值時,編碼爲 ziplist, 而當我插入一個較長的值,zset 的編程方式成爲了 skiplist.後端
對於跳躍表這個數據結構,其底層實現原理及代碼實現,本文就不細講了,若是不太清楚的讀者能夠看一下這個文章 跳錶的原理%E7%9A%84%E5%8E%9F%E7%90%86%E5%8F%8AConcurrentSkipListMap%E7%9A%84%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/#%E6%A6%82%E8%BF%B0), 或者自行 google 瞭解。數組
本文僅對 Redis 中跳躍表的實現作一個學習。服務器
首先讓咱們來看一下,skiplist 的定義:微信
typedef struct zskiplist{ // 表頭結點和尾節點 struct zskiplistNode *header, *tail; // 表中節點的數量 unsigned int length; // 表中層數最大的節點的層數 int level; } zskiplist;
這幾個屬性比較簡單,其中header, tail
能夠在 O(1) 的時間複雜度內定位到跳躍表的頭部和尾部,length
能夠在 O(1) 時間複雜度內獲得跳躍表的長度。level
能夠知道當前跳躍表最高的層,從而開始從高向低進行查找。數據結構
其中 skiplistNode 的節點的定義爲:性能
typedef struct zskiplistNode{ struct zskiplistLevel{ // 前進指針 struct zskiplistNode *forward; // 跨度 } level[]; // 後退指針 struct zskiplistNode *backward; // 分值 double score; // 成員對象 robj *obj; } zskiplistNode;
這個節點的定義有點東西的。學習
若是瞭解 Java 中的ConcurrentSkipListMap
的實現,或者看了上面個人那篇文章的話,就會知道,在 Java 中,一個 所謂的 節點(或者叫索引) 是有兩個指針的,一個指向右側的下一個索引,一個指向本身的下一層索引。
可是 Redis 不是這麼實現的,在上面的定義中,能夠看到zskiplistLevel
這個結構是一個數組,用一個數組來保存,本節點,以及本節點在全部層的索引
.
每一個索引中,有兩個屬性,
指向右側的指針,能夠在當前層,繼續向右走。
這個屬性設計的很巧妙,能夠用它來計算當前節點在 跳躍表中的一個排名,這就 zset 提供了查看排名的功能。
後退的指針,若是在高層索引向右走的太多了,能夠用後退指針來向後退。
這兩個屬性用來保存當前節點的真正值以及分值。
在 Java 中的ConcurrentSkipListMap
的實現中,索引每一次向上升級或者不升級,都是隨機的,所以:
...
而在 Redis 中,新添加一個節點時,會給該節點隨機一個索引層數,並且機率是 25%. 以後將該節點的各層索引與左右的索引相連接。
因爲機率是 25%, 所以 Redis 的跳躍表相對於 Java 中的跳躍表,結構更加扁平一些,在查找的時候,在同級索引上可能須要多查詢幾個。
也是由於結構扁平,所以索引的數量並非徹底的等同於節點數,額外的內存佔用只有 50%. 能夠爲 Redis 服務器節省一點內存。
咱們知道,在 zset 中,是能夠存儲分數同樣的值的,此時內部如何存儲?直接進行無序存儲嗎?
若是是這樣,當一個 zset 中,全部元素的分值都同樣,跳躍表表的性能就會退化成鏈表的性能嗎?
不是這樣的,Redis 除了按照分值排序以外,還會按照字符串的字典序來存儲。
前面提到了 跨度
這個屬性,當咱們須要查找某個元素的排名時,跳躍表首先開始一次查詢過程,找到該節點時,也能夠找到從頂層索引找到該節點的 查找路徑, 將 路徑上的全部節點的 跨度 值相加就是該節點的排名。
Redis 的跳躍表,和 其餘語言實現的跳躍表,整體思路同樣,在實現方式上有一些本身的小技巧。
跳躍表示 有序集合鍵 的底層實現之一,表中元素按照 score 大小進行排序,當 score 相同時,元素按照字符串的字典大小進行排序。
相比於 Java 的跳躍表,Redis 的跳躍表的索引層級更加扁平,能夠節省一些內存。
《Redis 的設計與實現(第二版)》
《Redis 深度歷險:核心原理和應用實踐》
完。
最後,歡迎關注個人我的公衆號【 呼延十 】,會不按期更新不少後端工程師的學習筆記。
也歡迎直接公衆號私信或者郵箱聯繫我,必定知無不言,言無不盡。
以上皆爲我的所思所得,若有錯誤歡迎評論區指正。
歡迎轉載,煩請署名並保留原文連接。
聯繫郵箱:huyanshi2580@gmail.com
更多學習筆記見我的博客或關注微信公衆號 < 呼延十 >------>呼延十