跳表不解决哈希冲突,它是基于随机分层链表的有序索引结构,适用于范围查询与高并发场景;其并发实现需权衡锁粒度、内存开销与复杂度,而非优化哈希冲突。

跳表(Skip List)本身不解决哈希冲突,也不能直接替代哈希表的冲突处理;把它和“哈希冲突优化”混在一起,是概念错配——你真正需要的,大概率是一个支持高并发读写的有序索引结构,而跳表只是其中一种选择。
为什么跳表和哈希冲突根本不是一回事
哈希冲突发生在哈希表中:相同 hash(key) 导致多个键映射到同一桶。解决方案通常是链地址法(拉链)、开放寻址(线性/二次探测)或动态扩容。跳表是完全不同的数据结构——它基于随机分层链表实现 O(log n) 的有序插入/查找/删除,天然无哈希、无桶、无冲突。
如果你在做并发索引,并希望避免哈希表的锁争用或 rehash 阻塞,跳表确实是个可行替代方案,但动机应是「需要有序性 + 并发友好」,而非「优化哈希冲突」。
- 哈希表适合 key 无序、纯随机访问场景;跳表适合范围查询、按序遍历、实时排名等
- 标准跳表不带哈希逻辑;强行在跳表节点里存
hash(key)并据此分层,既破坏跳表概率结构,又没解决任何哈希问题 - 所谓“哈希冲突优化的跳表”,常见于对论文或博客标题的误读——实际指的是用跳表替代传统哈希索引,在并发环境下获得更可预测的性能
并发跳表的核心锁策略与实操取舍
跳表并发难点不在查找(可无锁),而在插入/删除时需保证多层指针原子更新。主流做法是 per-node 锁 or optimistic retry,而不是全局锁。
-
std::shared_mutex按层级或按节点段加锁:读操作共享,写操作独占;但粒度粗,易成瓶颈 - 细粒度 node-level
std::mutex:每个节点自带锁,插入时只锁涉及的前驱节点(up to max_level),但内存开销上升 ~16–24 字节/节点 - 无锁跳表(如 Harris’ algorithm):依赖 CAS 操作 + 冗余标记位(
next指针低位用作删除标记),实现复杂,调试困难,GCC/Clang 对std::atomic生成代码质量影响大::compare_exchange_weak - 实践中建议从「带节点锁的跳表」起步:用
std::vector<:mutex>管理各层锁,或为每层维护独立锁,避免跨层竞争
Level 层高设置对并发性能的真实影响
跳表平均查找跳数 ≈ 2·log₂(n),但层高(max_level)直接影响最坏路径长度和内存占用。并发下它还决定锁竞争范围。
- 默认
max_level = 32(Redis ZSet)适合亿级节点,但小规模数据( - 每层新增节点时调用
rand() % 2(或std::bernoulli_distribution)决定是否向上延伸;不要用rand() % max_level——那会破坏概率平衡,导致上层稀疏、下层拥堵 - 并发插入时,若某层锁被长期持有(如长事务写入),高
max_level会让后续插入在低层等待更久;可考虑动态调整:监控level_count[i] / level_count[i-1]比值,偏离 0.5 时触发 rebalance(但生产环境慎用)
C++ 实现中几个容易崩的细节
跳表看似简单,C++ 实现并发版本时,几个点一碰就 core:
- 节点析构必须确保所有层的
next指针已置空,否则其他线程可能读到 dangling pointer;推荐用std::atomic存next,并在delete前 CAS 为nullptr -
std::shared_ptr管理节点生命周期?别用——引用计数本身的原子操作会成为新热点;改用 hazard pointer 或 epoch-based reclamation(如libcds的gc::HP) - 插入时先申请内存再加锁;否则锁内
new Node可能触发 malloc 争用,尤其在 jemalloc/tcmalloc 下表现更差 - 测试一定要覆盖「连续插入相同 key」场景:跳表通常要求 key 唯一,但你要明确是拒绝、覆盖还是允许重复——这直接影响
find_prev_nodes()返回逻辑和锁持有范围
跳表的并发价值不在理论复杂度,而在其天然的局部性:写操作只扰动 O(log n) 个节点,且这些节点在内存中往往相邻(分配器局部性)。但一旦你开始给每层加锁、引入 hazard pointer、做内存回收,那些“看起来简单”的指针操作,就会变成 cache line bouncing 和 false sharing 的温床。真正难的不是写出来,而是压测时发现 QPS 卡在 20 万就上不去,然后花三天才发现是 std::mutex 对齐错了,让两个 hot node 的锁落在同一个 cache line 上。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











