redis的zset數據結構:跳表


點贊再看,養成習慣,微信搜索「小大白日志」關注這個搬磚人。

文章不定期同步公眾號,還有各種一線大廠面試原題、我的學習系列筆記。


廣州這邊封閉式管理好久了,今天終於周末可以出去溜溜了
drawing

什么是zset

zset是redis中一種有序、不重復的數據類型,每個元素都有一個分值,它可用於實現排行榜單,其底層采用壓縮表ziplist或跳表skiplist的數據結構實現

zset的兩種數據結構

  • 壓縮表ziplist

當redis插入第一個元素時,同時滿足以下條件,就會以ziplist創建跳表

  • 節點數量<128 (可通過server.zset_max_ziplist_entries設置)
  • 節點的長度<64(可通過server.zset-max-zip以上任一個,都會list-value設置)

當選擇用ziplist實現zset后,以后插入的節點若不滿足以上任一個條件,就會轉為skiplist

  • 跳表skiplist

跳表的本質是一個多層鏈表,它能快速地查詢、插入、刪除【時間復雜度均為O(logn)】,所以它的查詢速度媲美平衡二叉樹,而且它的數據結構比平衡二叉樹簡單,結構示意圖如下:

image
特點:

  • 跳表的最底層擁有所有的元素
  • 跳表每一層都是一個鏈表,除了最底層是原始鏈表,層次逐漸往上可分別划分為一級索引層、二級索引層...
  • 跳表插入元素時,會先隨機生成出一個“層次數字”,然后元素會插入到這個層次的所有底層,直到原始鏈表層
  • 如果一個元素存在與某個索引層,那么這個元素也會存在於低於它的所有索引下層,如元素在第99索引層,那么由上到下從99索引層直到原始鏈表層都會存在該元素
  • 空間換時間,跳表查找變快了,但是要存儲許多索引層,故空間開銷變大了
 /**
 * 產生節點的高度。使用拋硬幣
 *
 * @return
 */
 private int getRandomLevel() {
     //可知,元素的插入層次i從1開始自增,隨機到哪一層的概率就像拋硬幣一樣,都是50%,故i越往后,其概率越小(每次都*0.5)
     //第一層概率:0.5,第二層0.5*0.5=0.25,...
        int lev = 1;
        while (random.nextInt() % 2 == 0) {
            lev++;
        }
        //MAX_LEVEL為跳表的最大層級
        return lev > MAX_LEVEL ? MAX_LEVEL : lev;
    }

跳表skiplist

  • 插入節點

    • 插入的時間復雜度為O(logn),每次插入都會先查找到要插入的位置(查找的時間復雜度就已經是【O(logn)】了,找到后直接插入【O(1)】,所以總的為【O(logn)】),刪除也是同理為O(logn)
    • 每個節點的插入層次是通過getRandomLevel()隨機出來的,插入層次互不影響

      以下模擬節點插入:
      image
  • 查找

查找節點時,從高索引層往低索引層查找:

一開始元素在高層從鏈表由前往后查找,直到要查找的目標元素在該層的某兩個相鄰元素之間,就會往下跳到下層的同一個位置,繼續從同一位置向鏈表尾方向遍歷查詢->重復上面的過程,直到查找到目標元素

查找時每一層都跳過部分元素,進而加快了查找效率,以下模擬節點查找:
image

OK,如果文章哪里有錯誤或不足,歡迎各位留言。
創作不易,各位的「三連」是二少創作的最大動力!我們下期見!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM