外观
一句话答案
跳表是多层有序链表,逐层跳跃实现 O(logn) 查找,Redis 选它因为实现简单、范围查询方便、并发友好。
核心要点
ZSet 的双重底层结构(同时维护两个):
ZSet
├── hashtable(哈希表):member → score 的映射
│ 作用:O(1) 查找某个 member 的 score(ZSCORE 命令)
│
└── skiplist(跳表):按 score 排序的有序索引
作用:O(log N) 范围查询、排名查询(ZRANGE、ZRANK 命令)为什么要同时维护两个结构?
- 只有跳表:
ZSCORE需要 O(log N),不够快 - 只有哈希表:无法做有序范围查询(ZRANGE、ZRANGEBYSCORE)
- 两者结合:以额外内存换取两种查询都 O(1)/O(log N)
编码切换(小数据量优化内存):
当同时满足以下两个条件时,使用 listpack(紧凑型双向链表,省内存):
- 元素数量 <= 128(默认)
- 每个元素 value 长度 <= 64 字节(默认)
超过任一阈值 → 转换为 skiplist + hashtable追问与易错
追问方向:
- ZSet 什么时候用 ziplist?
- 跳表层数怎么决定?
- 为什么概率是 1/4?
易错点:
- ❌ 跳表和红黑树性能一样——跳表范围查询更好
- ❌ ZSet 的 skiplist 就是标准跳表——Redis 做了优化
💡 记忆锚点
跳表 = 书的目录系统:底层是逐页翻(链表),加了章目录、节目录(多级索引)后O(logN)快速定位。Redis选跳表不选红黑树,因为跳表范围查询天然顺序遍历,实现简单,面试记住"实现简单+范围查询方便"两个理由。