redis原始碼分析之有序集SortedSet

2022-08-09 03:42:12 字數 4471 閱讀 4160

有序集sortedset算是redis中乙個很有特色的資料結構,通過這篇文章來總結一下這塊知識點。

redis中的有序集,允許使用者使用指定值對放進去的元素進行排序,並且基於該已排序的集合提供了一系列豐富的操作集合的api。

舉例如下:

//新增元素,table1為有序集的名字,100為用於排序字段(redis把它叫做score),a為我們要儲存的元素

127.0.0.1:6379> zadd table1 100 a

(integer) 1

127.0.0.1:6379> zadd table1 200 b

(integer) 1

127.0.0.1:6379> zadd table1 300 c

(integer) 1

//按照元素索引返回有序集中的元素,索引從0開始

127.0.0.1:6379> zrange table1 0 1

1) "a"

2) "b"

//按照元素排序範圍返回有序集中的元素,這裡用於排序的字段在redis中叫做score

127.0.0.1:6379> zrangebyscore table1 150 400

1) "b"

2) "c"

//刪除元素

127.0.0.1:6379> zrem table1 b

(integer) 1

在有序集中,用於排序的值叫做score,實際儲存的值叫做member。由於有序集中提供的api較多,這裡只舉了幾個常見的,具體可以參考redis文件。

void zaddcommand(client *c)
這裡可以看到流程轉向了zaddgenericcommand,並且傳入了乙個模式標記。

關於sortedset的操作模式這裡簡單說明一下,先來看一條完整的zadd命令:

zadd key [nx|xx] [ch] [incr] score member [score member ...]
其中的可選項我們依次看下:

nx表示如果元素存在,則不執行替換操作直接返回。

xx表示只操作已存在的元素。

ch表示返回修改(包括新增,更新)元素的數量,只能被zadd命令使用。

incr表示在原來的score基礎上加上新的score,而不是替換。

上面**片段中的zadd_none表示普通操作。

接下來看下zaddgenericcommand函式的原始碼,很長,耐心一點點看:

void zaddgenericcommand(client *c, int flags) 

//設定模式

int incr = (flags & zadd_incr) != 0;

int nx = (flags & zadd_nx) != 0;

int xx = (flags & zadd_xx) != 0;

int ch = (flags & zadd_ch) != 0;

//通過上面的解析,scoreidx為真實的初始score的索引位置

//這裡客戶端引數數量減去scoreidx就是剩餘所有元素的數量

elements = c->argc - scoreidx;

//由於有序集中score,member成對出現,所以加一層判斷

if (elements % 2 || !elements)

//這裡計算score,member有多少對

elements /= 2;

//引數合法性校驗

if (nx && xx)

//引數合法性校驗

if (incr && elements > 1)

//這裡開始解析score,先初始化scores陣列

scores = zmalloc(sizeof(double)*elements);

for (j = 0; j < elements; j++)

//這裡首先在client對應的db中查詢該key,即有序集

zobj = lookupkeywrite(c->db,key);

if (zobj == null) else

//加入db中

dbadd(c->db,key,zobj);

} else

}//這裡開始往有序集中新增元素

for (j = 0; j < elements; j++)

//記錄操作

if (retflags & zadd_added) added++;

if (retflags & zadd_updated) updated++;

if (!(retflags & zadd_nop)) processed++;

//設定新score值

score = newscore;

}//操作記錄

server.dirty += (added+updated);

//返回邏輯

reply_to_client:

if (incr) else

//清理邏輯

cleanup:

zfree(scores);

if (added || updated)

}

**有點長,來張圖看一下儲存結構:

注:每個entry都是由score+member組成

有了上面的結構圖以後,可以想到刪除操作應該就是根據不同的儲存結構進行,如果是ziplist就執行鍊錶刪除,如果是雜湊表+跳表結構,那就要把兩個集合都進行刪除。真實邏輯是什麼呢?

我們來看下刪除函式zremcommand的原始碼,相對短一點:

void zremcommand(client *c) 

}//同步操作

if (deleted)

//返回

addreplylonglong(c,deleted);

}

看下具體的刪除操作原始碼:

//引數zobj為有序集,ele為要刪除的元素

int zsetdel(robj *zobj, sds ele)

} else if (zobj->encoding == obj_encoding_skiplist)

} else

//沒有找到指定元素返回0

return 0;

}

最後看乙個查詢函式zrangecommand原始碼,也是很長,汗~~~,不過放心,有了上面的基礎,大致也能猜到查詢邏輯應該是什麼樣子的:

void zrangecommand(client *c) 

void zrangegenericcommand(client *c, int reverse) else if (c->argc >= 5)

//有序集校驗

if ((zobj = lookupkeyreadorreply(c,key,shared.emptymultibulk)) == null

|| checktype(c,zobj,obj_zset)) return;

//索引值重置

llen = zsetlength(zobj);

if (start < 0) start = llen+start;

if (end < 0) end = llen+end;

if (start < 0) start = 0;

//返回空集

if (start > end || start >= llen)

if (end >= llen) end = llen-1;

rangelen = (end-start)+1;

//返回給客戶端結果長度

addreplymultibulklen(c, withscores ? (rangelen*2) : rangelen);

//同樣是根據有序集的不同結構執行不同的查詢邏輯

if (zobj->encoding == obj_encoding_ziplist)

} else if (zobj->encoding == obj_encoding_skiplist) else

//遍歷並返回給客戶端

while(rangelen--)

} else

}

上面就是關於有序集sortedset的新增,刪除,查詢的原始碼。可以看出sortedset會根據存放元素的數量選擇ziplist或者雜湊表+跳表兩種資料結構進行實現,之所以原始碼看上去很長,主要原因也就是要根據不同的資料結構進行不同的**實現。只要掌握了這個核心思路,再看原始碼就不會太難。

有序集的邏輯不難,就是**有點長,涉及到ziplist,skiplist,dict三套資料結構,其中除了常規的dict之外,另外兩個資料結構內容都不少,準備專門寫文章進行總結,就不在這裡贅述了。本文主要目的是總結一下有序集sortedset的實現原理。

Redis原始碼分析 intset h c

intset.h c 是redis 的整數set實現,intset的結構體如下 基本結構 typedef struct intset intset intset的第乙個成員encoding,表明contents中的儲存資料的資料長度,可以是16bits,32bits,64bits。第二個成員leng...

Redis原始碼分析系列

redis目前熱門nosql記憶體資料庫,量不是很大,本系列是本人閱讀redis原始碼時記錄的筆記,由於時間倉促和水平有限,文中難免會有錯誤之處,歡迎讀者指出,共同學習進步,本文使用的redis版本是2.8.19。redis之hash資料結構 redis之intset資料結構 redis之skipl...

redis原始碼分析 adlist

typedef struct listnode listnode 首先定義了乙個節點,包含前驅和後繼以及對應的value typedef struct listiter listiter list的迭代器,next指標和迭代方向 typedef struct list list 鍊錶內容 head和...