redis 系列7 數據結構之跳躍表

来源:https://www.cnblogs.com/MrHSR/archive/2018/11/10/9936293.html
-Advertisement-
Play Games

一.概述 跳躍表(skiplist)是一種有序數據結構,它通過在每個節點中維持多個指向其他節點的指針,從而達到快速訪問節點的目的。在大部分情況下,跳躍表的效率可以和平衡樹(關係型資料庫的索引就是平衡樹結構)相媲美,並且因為跳躍表的實現比平衡樹要來得更為簡單,所以有不少程式使用跳躍表來代替平衡樹。 R ...


一.概述

  跳躍表(skiplist)是一種有序數據結構,它通過在每個節點中維持多個指向其他節點的指針,從而達到快速訪問節點的目的。在大部分情況下,跳躍表的效率可以和平衡樹(關係型資料庫的索引就是平衡樹結構)相媲美,並且因為跳躍表的實現比平衡樹要來得更為簡單,所以有不少程式使用跳躍表來代替平衡樹。

  Redis使用跳躍表作為"有序集合鍵"的底層實現之一,如果一個有序集合包含的元素數量比較多,又或者有序集合中元素的成員是比較長的字元串時,Redis就會使用跳躍表來作為有序集合鍵的底層實現。

  下麵用到的命令zadd和zrange。 使用zadd 命令將多個成員(number)及其score值加入到有序集key中。number是有序集成員,score可以是整數值或雙精度浮點數。使用zrange命令將返回有序集合中給定區間的元素,start從0開始,stop 結束下標。

    -- zadd命令語法格式
    ZADD key score member [[score member] [score member] ...]
    -- zrange命令語法格式
    ZRANGE key start stop [WITHSCORES]

  例1:下麵使用zadd將fruit-price作為一個有序集合鍵,每個節點元素包括score和number。其中 score是價格,number是水果名稱。再使用zrange 讀出有序集合元素。

127.0.0.1:6379> zadd fruit-price 5.0 banana 6.5 cherry 8.0 apple
        (integer) 3
127.0.0.1:6379> zadd fruit-price 4.0 pear
        (integer) 1
127.0.0.1:6379> zrange  fruit-price 0 3 withscores
        1) "pear"
        2) "4"
        3) "banana"
        4) "5"
        5) "cherry"
        6) "6.5"
        7) "apple"
        8) "8"

  在上例中fruit-price有序集合的所有數據都保存在一個跳躍表裡面,其中每個跳躍表節點都保存了一款水果的價格信息,所有水果按價錢從低到高在跳躍表裡面排序。對比鏈表和字典等數據結構在Redis內部廣泛應用不同,Redis只在兩個地方用到了跳躍表,一個是實現有序集合鍵,另一個是在集群節點中用作內部數據結構。

 

  1.1 跳躍表的實現

    Redis跳躍表由 redis.h/zskiplistNode和redis.h/zskiplist 兩個結構定義,其中zskiplistNode結構用於表示跳躍表節點,  而zskiplist結構則用於保存跳躍表節點的相關信息,比如節點數量,以及指向表頭節點和表尾節點的指針等等。

    上圖中展示了一個跳躍表示例,位於是左邊的是zskiplist結構,該結構包括以下屬性:

(1) header: 指向跳躍表的表頭節點。這裡為第一個zskiplistNode。

(2) tail : 指向跳躍表的表尾節點。這裡為第四個zskiplistNode。

(3) level:記錄目前跳躍表內,zskiplistNode節點中最大的層數(表頭節點的層數不計算在內)。最大節點的層數是第四個zskiplistNode節點,值為5 (每個跳躍表節點的層高都是1到32之間的隨機數)。

(4) length: 記錄跳躍表的長度。也就是節點數量(表頭節點不計算在內),這裡值是3。

    上圖中右方四個zskiplistNode節點,包含以下屬性:   

      (1) 層level :  每個節點中用L1,L2,L3等字樣標記節點的各個層,每個層都帶有兩個屬性,包括前進指針和跨度。在上圖裡連線上帶有數字的箭頭就代表前進指針, 而那個數字就是跨度。當程式從表頭向表尾進行遍歷時,訪問會沿著層的前進指針進行。

      (2) 後退(backward)指針: 節點中用BW字樣標記節點的後退指針,後退指針在程式從表尾向表頭遍歷時使用。

      (3)分值(socre) : 各個節點中的1.0,2.0,3.0節點所保存的分值,在跳躍表中,節點按各自所保存的分值從小到大排列。

      (4)成員對象(obj): 各個節點中的01,02,03是節點所保存的成員對象。

   

  1.2 跳躍表節點

    下麵對zskiplistNode和zskiplist兩個結構進行更詳細的介紹,跳躍表節點實現由redis.h/zskiplistNode結構定義。

typedef struct zskiplistNode{

            //
            struct zskiplistNode{
                //前進指針
                struct zskiplistNode *forward;
                //跨度
                unsigned int span;
            }level[];

            //後退指針   
            struct zskiplistNode *backward;
            //分值
            double score;
            //成員對象
            robj *obj;

        }zskiplistNode;

    (1) 層:跳躍表節點的level數組可以包含多個元素,每個元素都包含一個指向其他節點的指針,程式可以通過這些層來加快訪問其他節點的速度,一般來說,層的數量越多,訪問其他節點的速度就越快。

    (2) 前進指針:每個層都有一個指向表尾方向的前進指針(level[i].forward屬性),用於從表頭向表尾方向訪問節點。

    如上圖所示: 遍歷是程式首先訪問跳躍表的第一個節點(表頭),然後從第四層(L4)的前進指針移動到表中的第二個節點。在第二個節點時,程式沿著第二層(L2)的前進指針移動到表中的第三個節點。在第三個節點時,程式同樣沿著第二層(L2)的前進指針移動到表中的第四個節點。當程式再次沿著第四個節點前進指針移動時,遇到null,程式知道這時已經到達了跳躍表的表尾,於是結束這次遍歷。

    (3) 跨度:層的跨度(level[i].span屬性)用於記錄兩個節點之間的距離:兩個節點之間的跨度越大,它們相距就越遠。 指向null的所有前進指針跨度都為0,因為它們沒有連向任何節點。對於遍歷操作只使用前進指針就可以完成了,跨度實際上是用來計算排位的。

    (4) 後退指針:節點的後退指針(backward屬性) 用於從表尾向表頭方向訪問節點。與前進指針不同,前進指針一次可以跳過多個節點,而每個節點只有一個後退指針,所以每次只能後退至前一個節點。

    (5) 分值和成員:節點的分值(score屬性)是一個double類型的浮點數,跳躍表中的所有節點都按分值從小到大來排序。節點的成員對象(obj屬性)是一個指針,它指向一個字元串對象,而字元串對象則保存著一個SDS值。在同一個跳躍表中,各個節點保存的成員對象必須是唯一的,但是多個節點保存的分值卻可以是相同的,分值相同的節點將按照成員對象在字典中的大小來進行排序,成員對象較小的節點會排在前面(靠近表頭的方向)。

例2:  分值相同的,按成員對象來排序。
    127.0.0.1:6379> zadd test 1.0 a
    (integer) 1
    127.0.0.1:6379> zadd test 1.0 c
    (integer) 1
    127.0.0.1:6379> zadd test 1.0 b
    (integer) 1
    127.0.0.1:6379> zrange test 0 2 withscores
    1) "a"
    2) "1"
    3) "b"
    4) "1"
    5) "c"
    6) "1"

    

   1.3 跳躍表

    僅靠多個跳躍表節點就可以組成一個跳躍表,但通過使用一個zskplist結構來持有這些節點,程式可以更方便地對整個跳躍表進行處理。

typedef struct zskiplist
        {
            //表頭節點和表尾節點
            struct skiplistNode *header, *tail;
            //表中節點數量
             unsigned long length;
            //表中層數最大的節點的層數
              int level;
        }zskiplist

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 好的表結構分的比較細緻,個人理解大概主要分為主表、明細、歷史記錄表、中間表,輔助表結構應該分為:類型表、狀態表、統計表、統計明細表等。為了一個功能加那麼多表實在是多餘,如果寫一個非常複雜的業務邏輯還是很有必要的,因為要做到物帳聯動。這可能不是一個明智的選擇,還有一種方案是儘可能的壓縮表結構,少分一些 ...
  • 作者:依樂祝 原文地址:https://www.cnblogs.com/yilezhu/p/9941208.html 作者:大石頭 時間:2018 11 10 晚上20:00 地點:釘釘群(組織代碼BKMV7685)QQ群:1600800 內容:Redis基本使用及百億數據量中的使用技巧分享 記錄人 ...
  • Greenplum支持原有主機擴展Segment個數、新增主機、和混合擴展 本文以在已有機器上擴展節點為例 1、可按照hostname:address:port:fselocation:dbid:content:preferred_role:replication_port來配置擴展文件 2、執行命 ...
  • 9. 查詢備份還原資料庫的進度。 select command ,percent_complete ,est_time_to_go=convert(varchar,(estimated_completion_time/3600000))+' hour, ' +convert(varchar,(est ...
  • 一:分析函數overOracle從8.1.6開始提供分析函數,分析函數用於計算基於組的某種聚合值,它和聚合函數的不同之處是對於每個組返回多行,而聚合函數對於每個組只返回一行。 1、分析函數和聚合函數的不同之處: 分析函數和聚合函數很多是同名的,意思也一樣,只是聚合函數用group by分組,每個分組 ...
  • 原文地址:https://www.cnblogs.com/moyijian/p/9940323.html#4111551 級聯刪除,比如你刪除某個表的時候後面加這個關鍵字,會在刪除這個表的同時刪除和該表有關係的其他對象 1.級聯刪除表中的信息,當表A中的欄位引用了表B中的欄位時,一旦刪除B中該欄位的 ...
  • in 和exists 對於以上兩種查詢條件,in是把外表和內表作hash 連接,而exists 是對外表作loop 迴圈,每次loop 迴圈再對內表進行查詢。 一直以來認為exists 比in 效率高的說法是不准確的。在不同的情況下,exists與in的性能各有優缺項,如果查詢的兩個表大小相當,那麼 ...
  • 啟動命令帶了這個參數:redis.windows.conf,由於我測試環境是windows平臺,所以是這個,有的是redis.conf。顧名思義,redis.conf就是配置文件,然後啟動時加上這個配置文件名的意思就是以配置文件里的配置參數來啟動,比如你設置了密碼,那麼客戶端連接就得輸入密碼了,等等... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...