更多精彩文章,關注【ToBeTopJavaer】,更有數萬元精品vip資源免費等你來拿!!!
本文咱們要剖析的基本類型是ZSet,下面咱們將深刻源碼剖析Redis中ZSet的實現。php
一、sorted set,有序的 set,每一個元素有個 score。java
數據結構對比
zadd myzset 10 java 20 php 30 ruby 40 cpp 50 python複製代碼
zrange myzset 0 -1 withscoreszrevrange myzset 0 -1 withscores複製代碼
zrangebyscore myzset 20 30複製代碼
zrem myzset php cpp複製代碼
zcard myzset複製代碼
zincrby myzset 5 python複製代碼
zcount myzset 20 60複製代碼
zrank myzset java複製代碼
zsocre myzset java複製代碼
同時知足如下條件時使用 ziplist 編碼:python
一、元素數量小於 128 個redis
二、全部 member 的長度都小於 64 字節算法
對應 redis.conf 參數:數組
在這樣一個鏈表中,若是咱們要查找某個數據,那麼須要從頭開始逐個進行比較,直到找到包含數據的那個節點,或者找到第一個比給定數據大的節點爲止(沒找到)。ruby
也就是說,時間複雜度爲 O(n)。一樣,當咱們要插入新數據的時候,也要經歷一樣的查找過程,從而肯定插入位置。bash
而二分查找法只適用於有序數組,不適用於鏈表。數據結構
假如咱們每相鄰兩個節點增長一個指針(或者理解爲有三個元素進入了第二層),讓指針指向下下個節點。dom
這樣全部新增長的指針連成了一個新的鏈表,但它包含的節點個數只有原來的一半(上圖中是 7, 19, 26)。在插入一個數據的時候,決定要放到那一層,取決於一個算法(在 redis 中 t_zset.c 有一個 zslRandomLevel 這個方法)。
如今當咱們想查找數據的時候,能夠先沿着這個新鏈表進行查找。當碰到比待查數據大的節點時,再回到原來的鏈表中的下一層進行查找。好比,咱們想查找 23,查找的路徑是沿着下圖中標紅的指針所指向的方向進行的:
1. 23 首先和 7 比較,再和 19 比較,比它們都大,繼續向後比較。
2. 但 23 和 26 比較的時候,比 26 要小,所以回到下面的鏈表(原鏈表),與 22比較。
3. 23 比 22 要大,沿下面的指針繼續向後和 26 比較。23 比 26 小,說明待查數據 23 在原鏈表中不存在。
typedef struct zskiplistNode {
sds ele; /* zset 的元素 */
double score; /* 分值 */
struct zskiplistNode *backward; /* 後退指針 */
struct zskiplistLevel {
struct zskiplistNode *forward; /* 前進指針, 對應 level 的下一個節點 */
unsigned long span; /* 從當前節點到下一個節點的跨度(跨越的節點數) */
} level[]; /* 層 */
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail; /* 指向跳躍表的頭結點和尾節點 */
unsigned long length; /* 跳躍表的節點數 */
int level; /* 最大的層數 */
} zskiplist;
typedef struct zset {
dict *dict;
zskiplist *zsl;
} zset;
複製代碼
int zslRandomLevel(void) {
int level = 1;
while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
level += 1;
return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}複製代碼
更多精彩文章,關注【ToBeTopJavaer】,更有數萬元精品vip資源免費等你來拿!!!