Redis 跳表(Skip List)原理實(shí)現(xiàn)
引言:為什么 Redis 選擇跳表?
在有序集合(ZSET)的實(shí)現(xiàn)中,Redis 開發(fā)者面臨一個(gè)關(guān)鍵抉擇:如何在高性能讀寫和代碼簡潔性之間找到平衡?傳統(tǒng)平衡樹(如紅黑樹)雖然能保證 O(logN) 時(shí)間復(fù)雜度,但實(shí)現(xiàn)復(fù)雜且難以支持范圍查詢。跳表(Skip List) 以媲美平衡樹的性能、極簡的實(shí)現(xiàn)(約 200 行代碼)和天然支持范圍查詢的特性,成為 Redis ZSET 的核心數(shù)據(jù)結(jié)構(gòu)。本文將深入剖析跳表的實(shí)現(xiàn)細(xì)節(jié)與 Redis 的工程優(yōu)化。
一、跳表核心思想:概率化的多層索引
1.1 從鏈表到跳表的進(jìn)化
- 普通鏈表:插入/刪除 O(1),但查詢需要 O(N)
- 跳表創(chuàng)新:通過隨機(jī)化多層索引,實(shí)現(xiàn)對數(shù)級查詢效率
1.2 跳表結(jié)構(gòu)可視化
Level 3: Head -> 37 --------------------------> 99 -> NULL Level 2: Head -> 37 -------> 71 -------> 99 -> NULL Level 1: Head -> 37 -> 55 -> 71 -> 85 -> 99 -> NULL Level 0: Head -> 37 -> 55 -> 71 -> 85 -> 99 -> NULL
關(guān)鍵特性:
每個(gè)節(jié)點(diǎn)隨機(jī)生成層數(shù)(Redis 最大層數(shù) 64)
高層索引跨越更多節(jié)點(diǎn),加速搜索
底層鏈表存儲完整數(shù)據(jù)
二、Redis 跳表實(shí)現(xiàn)深度解剖
2.1 數(shù)據(jù)結(jié)構(gòu)定義(redis.h)
// 跳表節(jié)點(diǎn)
typedef struct zskiplistNode {
sds ele; // 成員對象(SDS字符串)
double score; // 排序分值
struct zskiplistNode *backward; // 后退指針(雙向鏈表)
struct zskiplistLevel {
struct zskiplistNode *forward; // 前進(jìn)指針
unsigned long span; // 跨度(用于排名計(jì)算)
} level[]; // 柔性數(shù)組,層級隨機(jī)生成
} zskiplistNode;
// 跳表結(jié)構(gòu)
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length; // 節(jié)點(diǎn)總數(shù)
int level; // 當(dāng)前最大層數(shù)
} zskiplist;設(shè)計(jì)亮點(diǎn):
- span 字段:記錄節(jié)點(diǎn)在某一層的跨度,支持 O(1) 時(shí)間復(fù)雜度計(jì)算元素排名(
ZRANK) - backward 指針:構(gòu)成雙向鏈表,支持逆序遍歷
- 柔性數(shù)組(level[]):內(nèi)存緊湊,避免指針冗余
2.2 關(guān)鍵操作源碼解析
2.2.1 節(jié)點(diǎn)層數(shù)生成算法
// redis.h 源碼節(jié)選
int zslRandomLevel(void) {
int level = 1;
// 0xFFFF 對應(yīng) 1/4 概率提升層級(基于位運(yùn)算優(yōu)化)
while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
level += 1;
return (level < ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}數(shù)學(xué)原理:
- 使用 冪次定律(Power Law),高層節(jié)點(diǎn)指數(shù)級減少
- 每個(gè)節(jié)點(diǎn)有 50% 概率進(jìn)入 L1,25% 進(jìn)入 L2,12.5% 進(jìn)入 L3...
- Redis 實(shí)際使用 1/4 的概率因子(優(yōu)化內(nèi)存與性能平衡)
2.2.2 插入節(jié)點(diǎn)流程(zslInsert)
- 搜索路徑記錄:從最高層開始,記錄每層的前驅(qū)節(jié)點(diǎn)和跨度
- 生成隨機(jī)層數(shù):調(diào)用 zslRandomLevel
- 創(chuàng)建新節(jié)點(diǎn):分配層級并連接前后指針
- 更新跨度:調(diào)整相鄰節(jié)點(diǎn)的 span 值
- 維護(hù)后退指針:設(shè)置新節(jié)點(diǎn)的 backward 指針
2.2.3 范圍查詢(ZRANGEBYSCORE)
- 從高層索引快速定位起點(diǎn)
- 利用底層鏈表遍歷范圍
- 復(fù)雜度:O(logN + M)(M 為返回元素?cái)?shù)量)
三、性能分析與優(yōu)化策略
3.1 時(shí)間復(fù)雜度對比
| 操作 | 跳表(平均) | 跳表(最壞) | 平衡樹 |
|---|---|---|---|
| 插入 | O(logN) | O(N) | O(logN) |
| 刪除 | O(logN) | O(N) | O(logN) |
| 查找 | O(logN) | O(N) | O(logN) |
| 范圍查詢 | O(logN + M) | O(N) | O(logN + M) |
注:跳表最壞情況(所有節(jié)點(diǎn)高度相同)概率極低(例如 1億節(jié)點(diǎn)出現(xiàn)概率為 1/(2^50))
3.2 內(nèi)存占用分析
- 理論空間復(fù)雜度:O(NlogN)
- Redis 優(yōu)化實(shí)踐:通過 1/4 概率因子,實(shí)際空間占用約為 O(1.33N)(實(shí)測 100 萬節(jié)點(diǎn)內(nèi)存約 64MB)
3.3 調(diào)優(yōu)參數(shù)
- ZSKIPLIST_MAXLEVEL:控制最大層數(shù)(默認(rèn) 64,可調(diào)整內(nèi)存與性能平衡)
- ZSKIPLIST_P:調(diào)整層數(shù)生成概率(默認(rèn) 0.25)
四、跳表在 Redis 中的應(yīng)用場景
4.1 有序集合(ZSET)
元素?cái)?shù)量 > 128 或 元素長度 > 64 字節(jié) 時(shí),ZSET 內(nèi)部使用跳表
支持操作:
ZADD/ZREM:插入刪除ZRANK/ZSCORE:排名查詢ZRANGE:范圍查詢
4.2 集群元數(shù)據(jù)管理
用于維護(hù)槽位(slot)與節(jié)點(diǎn)的映射關(guān)系
五、跳表 vs 平衡樹:工程角度的選擇
| 維度 | 跳表 | 紅黑樹 |
|---|---|---|
| 實(shí)現(xiàn)復(fù)雜度 | 約 200 行代碼 | 約 500 行代碼 |
| 范圍查詢 | 天然支持(鏈表特性) | 需要額外遍歷 |
| 并發(fā)控制 | 更易實(shí)現(xiàn)無鎖優(yōu)化 | 需要復(fù)雜鎖機(jī)制 |
| 調(diào)試難度 | 可視化調(diào)試友好 | 樹旋轉(zhuǎn)邏輯難追蹤 |
Redis 作者 Antirez 的評價(jià):“跳表在理論上不如平衡樹優(yōu)雅,但實(shí)際工程中更簡單、更快,尤其適合需要范圍查詢的場景。”
總結(jié):
跳表的精妙之處在于 用概率換結(jié)構(gòu),通過隨機(jī)化層級分布避免復(fù)雜的再平衡操作。這種“以空間換時(shí)間” + “以概率換簡單性”的設(shè)計(jì)哲學(xué),在分布式系統(tǒng)開發(fā)中具有重要借鑒意義。理解跳表不僅有助于掌握 Redis 源碼,更能啟發(fā)我們思考如何在高性能與可維護(hù)性之間找到平衡。
到此這篇關(guān)于Redis 跳表(Skip List)原理實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Redis 跳表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring?boot集成redis基礎(chǔ)入門實(shí)例詳解
redis在spring?boot項(xiàng)目開發(fā)中是常用的緩存套件,常見使用的是spring-boot-starter-data-redis,這篇文章主要介紹了spring?boot集成redis基礎(chǔ)入門,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10
Centos7.3安裝Redis4.0.6詳細(xì)圖文教程
這篇文章主要介紹了Centos7.3安裝Redis4.0.6詳細(xì)教程圖解,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-10-10
Redis持久化方式之RDB和AOF的原理及優(yōu)缺點(diǎn)
在Redis中,數(shù)據(jù)可以分為兩類,即內(nèi)存數(shù)據(jù)和磁盤數(shù)據(jù),Redis?提供了兩種不同的持久化方式,其中?RDB?是快照備份機(jī)制,AOF?則是追加寫操作機(jī)制,本文將詳細(xì)給大家介紹Redis?持久化方式RDB和AOF的原理及優(yōu)缺點(diǎn),感興趣的同學(xué)可以跟著小編一起來學(xué)習(xí)2023-06-06
RabbitMQ+redis+Redisson分布式鎖+seata實(shí)現(xiàn)訂單服務(wù)的流程分析
訂單服務(wù)涉及許多方面,分布式事務(wù),分布式鎖,例如訂單超時(shí)未支付要取消訂單,訂單如何防止重復(fù)提交,如何防止超賣、這里都會使用到,這篇文章主要介紹了RabbitMQ+redis+Redisson分布式鎖+seata實(shí)現(xiàn)訂單服務(wù)的流程分析,需要的朋友可以參考下2024-07-07

