redis中跳表zset的具體使用
跳表的基本思想
Skip List(跳躍列表)這種隨機的數(shù)據(jù)結(jié)構(gòu),可以看做是一個二叉樹的變種,它在性能上與紅黑樹、AVL樹很相近;但是Skip List(跳躍列表)的實現(xiàn)相比前兩者要簡單很多,目前Redis的zset實現(xiàn)采用了Skip List(跳躍列表)。
特點
1、分層,每層由有序鏈表構(gòu)成
2、頭節(jié)點在每層出現(xiàn)
3、某個節(jié)點如果在上層出現(xiàn),那在下層也出現(xiàn)
4、節(jié)點的層數(shù)是隨機的
節(jié)點與結(jié)構(gòu)
跳躍表節(jié)點zskiplistNode
/* ZSETs use a specialized version of Skiplists */ typedef struct zskiplistNode { sds ele; double score; struct zskiplistNode *backward; struct zskiplistLevel { struct zskiplistNode *forward; unsigned long span; } level[]; } zskiplistNode;
屬性
- ele:存儲字符串?dāng)?shù)據(jù)
- score:存儲排序分值
- *backward:指針,指向當(dāng)前節(jié)點最底層的前一個節(jié)點
- level[]:柔性數(shù)組,隨機生成1-64的值
- forward:指向本層下一個節(jié)點
- span:本層下個節(jié)點到本節(jié)點的元素個數(shù)
跳躍表鏈表
typedef struct zskiplist { struct zskiplistNode *header, *tail; unsigned long length; int level; } zskiplist;
屬性
- header,tail:頭節(jié)點和尾節(jié)點
- length:跳躍表長度(不包括頭節(jié)點)
- tail:跳躍表高度
跳表的設(shè)計思想和優(yōu)勢
1、能夠同時擁有鏈表和數(shù)優(yōu)勢的數(shù)據(jù)結(jié)構(gòu),既有鏈表插入快的特點又有數(shù)組查詢快的特點
2、隨機跨越層數(shù)
3、最底層的鏈表是雙向鏈表,包含所有元素
4、對于有序鏈表查詢優(yōu)化,相比較于平衡數(shù)來說,更好實現(xiàn)
5、內(nèi)存占用上來看,相比較于平衡數(shù)會更少
API解析
Tip:以下的zsl為zskiplist
zslCreate(創(chuàng)建跳躍表)
/* Create a new skiplist. */ zskiplist *zslCreate(void) { int j; zskiplist *zsl; zsl = zmalloc(sizeof(*zsl)); zsl->level = 1; zsl->length = 0; zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL); for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) { zsl->header->level[j].forward = NULL; zsl->header->level[j].span = 0; } zsl->header->backward = NULL; zsl->tail = NULL; return zsl; }
大致流程:
1、定義一個zsl,申請內(nèi)存,賦初始值
2、調(diào)用zslCreateNode創(chuàng)建出頭節(jié)點
3、每層頭節(jié)點賦初始值
4、尾節(jié)點賦null值
zslCreateNode(創(chuàng)建節(jié)點)
/* Create a skiplist node with the specified number of levels. * The SDS string 'ele' is referenced by the node after the call. */ zskiplistNode *zslCreateNode(int level, double score, sds ele) { zskiplistNode *zn = zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel)); zn->score = score; zn->ele = ele; return zn; }
大致流程:
1、申請內(nèi)存(節(jié)點內(nèi)存和柔性數(shù)組的內(nèi)存)
2、屬性賦值
zslGetRank(查找排位)
排位就是累積跨越的節(jié)點數(shù)量
unsigned long zslGetRank(zskiplist *zsl, double score, sds ele) { zskiplistNode *x; unsigned long rank = 0; int i; x = zsl->header; for (i = zsl->level-1; i >= 0; i--) { while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && sdscmp(x->level[i].forward->ele,ele) <= 0))) { rank += x->level[i].span; x = x->level[i].forward; } /* x might be equal to zsl->header, so test if obj is non-NULL */ if (x->ele && x->score == score && sdscmp(x->ele,ele) == 0) { return rank; } } return 0; }
大致流程:
1、從最上層開始遍歷節(jié)點并對比元素,對比score
2、如果當(dāng)前分值大雨下一個分值,則累加span(比對分值,如果分值一樣就比對ele)
3、指向本層的下一個節(jié)點
4、如果找到了,也就是ele相同,則返回
zslDelete(刪除節(jié)點)
int zslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node) { zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x; int i; x = zsl->header; for (i = zsl->level-1; i >= 0; i--) { while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && sdscmp(x->level[i].forward->ele,ele) < 0))) { x = x->level[i].forward; } update[i] = x; } /* We may have multiple elements with the same score, what we need * is to find the element with both the right score and object. */ x = x->level[0].forward; if (x && score == x->score && sdscmp(x->ele,ele) == 0) { zslDeleteNode(zsl, x, update); if (!node) zslFreeNode(x); else *node = x; return 1; } return 0; /* not found */ }
大致流程:
1、遍歷跳表
2、比對分值,比對ele
3、如分值小于或等于當(dāng)前值,并且ele不相等,繼續(xù)下一個并記錄節(jié)點
4、如分值和ele都相同,調(diào)用zslDeleteNode刪除該節(jié)點
跳表是在很多排名以及分?jǐn)?shù)相關(guān)的場景中使用頻率極高的數(shù)據(jù)結(jié)構(gòu),也是設(shè)計的極其巧妙的一種結(jié)構(gòu),希望本篇文章能幫助各位更加深入的理解這種結(jié)構(gòu)。
到此這篇關(guān)于redis中跳表zset的具體使用的文章就介紹到這了,更多相關(guān)redis 跳表zset內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于在Redis中使用Pipelining加速查詢的問題
這篇文章主要介紹了在Redis中使用Pipelining加速查詢,Redis是一個client-server模式的TCP服務(wù),也被稱為Request/Response協(xié)議的實現(xiàn),本文通過一個例子給大家詳細(xì)介紹,感興趣的朋友一起看看吧2022-05-05解決redis-cli報錯Could not connect to Redis&
這篇文章主要介紹了解決redis-cli報錯Could not connect to Redis at 127.0.0.1:6379: Connection refused,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-04-04Redis+Lua腳本實現(xiàn)計數(shù)器接口防刷功能(升級版)
這篇文章主要介紹了Redis+Lua腳本實現(xiàn)計數(shù)器接口防刷功能,使用腳本使得set命令和expire命令一同達(dá)到Redis被執(zhí)行且不會被干擾,在很大程度上保證了原子操作,對Redis實現(xiàn)計數(shù)器接口防刷功能感興趣的朋友一起看看吧2022-02-02如何監(jiān)聽Redis中Key值的變化(SpringBoot整合)
測試過程中我們有一部分常量值放入redis,共大部分應(yīng)用調(diào)用,但在測試過程中經(jīng)常有人會清空redis,回歸測試,下面這篇文章主要給大家介紹了關(guān)于如何監(jiān)聽Redis中Key值變化的相關(guān)資料,需要的朋友可以參考下2024-03-03