亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

1. 前言

上一章節介紹了 SDS 數據結構,即 Redis 最基礎的 Key-Value 存儲實現,本章節繼續介紹 Redis 底層的高級數據結構。

Redis 的五種基本結構中還有一個叫做 zset 的數據結構,zset 保證了每個值的唯一性,這方面性質同傳統的 set 集合,也可以對每個值賦予 score,按照 score 進行排序。這種高級性質依賴于底層的跳躍表數據結構實現。

2. SkipList

2.1 SkipList 數據結構

面試官提問: Redis zset 數據結構的底層實現是什么?為什么要使用跳躍表?

題目解析:

在介紹跳躍表(SkipList,簡稱跳表)之前,我們可以以單鏈表數據結構作為對比。

圖片描述

(單鏈表結構示意圖)

在單鏈表中,我們查詢一個元素的時間復雜度是 O (N),其中 N 是鏈表的長度,因為需要誒個遍歷節點,單鏈表不支持數組的隨機插入和刪除,也不支持數組的自動排序,顯然不適合作為 zset 的實現方式。

跳躍表的基礎定義可參考維基百科定義

參考定義,我們給出 C 語言的結構體定義:

typedef struct zskiplistNode {
    sds ele;
    double score;
    struct zskiplistNode *backward;
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        unsigned int span;
    } level[];
} zskiplistNode;

typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length;
    int level;
} zskiplist;

候選人需要描述跳表的數據結構,可以通過畫圖或者其他方式給出定義。

圖片描述
?

(典型的跳躍表結構,圖片來自網絡,侵刪)

從結構體可以看出,節點有不同的定義:

  • sds:存儲的字符串對象;
  • score:分數,跳表中所有節點按照 score 由大到小排列;
  • backward:指向后退節點的指針;
  • forward:指向下一個節點的指針;
  • level:數組,數組中的每一個元素包括了指向其他節點的指針,level 的長度在 1 到 32(2^5)之間。

其中跳表的主要組成結構有:

  • 表頭:表示跳表的入口;
  • 表尾:表示跳表的尾部,數值全部都是 NULL;
  • 節點:保存具體數值,并且具有層結構;
  • 層:就是上述 level 定義中的單個元素,保存前一個節點的指針,以及該層下個節點向前跨越的數值(span)。

跳表的查詢過程本質上是自上而下的二分查找,插入和查詢過程都相對復雜,這里不做贅述。

在闡述基本定義之后,我們需要關注跳躍表的核心特點:

  • 本質是隨機化數據結構,可以在對數(logN)時間內完成對數據的查找、插入、刪除操作;
  • 跳躍表在 Redis 的唯一應用,就是作為有序集合支撐底層數據結構。

2.2 跳躍表 vs 二叉樹

面試官提問: 二叉查找樹也能實現對數時間的查找插入特性,為什么還要使用跳躍表?

題目解析:

面試官經常會拿跳表和其他數據結構進行對比,依次同時考察候選人數據結構的基礎能力。

雖然二叉查找樹(Binary Search Tree)也滿足插入、查找、刪除時間復雜度為 O (logN),但是在極端情況下,如果插入的數據滿足完全有序(例如 1,2,3,4 …),則每次插入二手樹都是右節點,這時二叉查找樹會退化為線性鏈表,查詢時間復雜度退化為 O(N)。相對于二叉查找樹,跳躍表的表現更穩定。

2.3 跳躍表 vs 紅黑樹

面試官提問: 紅黑樹也能實現對數時間的查找插入特性,為什么還要使用跳躍表?

題目解析:

紅黑樹(Red Black Tree)是二叉查找樹的一種變形,查找、插入、刪除的時間復雜度也是 O (logN),為什么 Redis 不使用紅黑樹作為 zset 的數據結構實現?

關于這一點,Redis 的官方解釋已經很全面:

There are a few reasons:

  1. They are not very memory intensive. It’s up to you basically. Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive than btrees.
  2. A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list. With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees.
  3. They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code.

總結下來就是兩點原因:

(1)相對于 B 樹 / 紅黑樹,跳躍表修改節點對內存的變量影響?。?/p>

(2)性能接近紅黑樹,但是跳表編碼實現更簡單,方便調試,簡單來說就是編碼完整的紅黑樹實現麻煩且不直觀。

3. 小結

本章節介紹了跳躍表的基本數據結構定義、插入查找的時間復雜度,以及和其他數據結構的對比。跳表是常見的 Redis 底層數據結構,希望大家能夠深入理解并掌握。