为什么redis使用跳表mysql使用b+树:几种搜索的数据结构的对比

1.只读场景下的高效查找

纯内存、数据随机分布 、且只关注查找性能 的前提下,AVL 树(二叉平衡树)通常是搜索效率最高的选择

  • AVL 树通过严格的平衡策略(任意节点左右子树高度差 ≤ 1),将树高控制在理论最小值 ⌊log₂n⌋。

  • 这意味着每次查找所需的比较次数最少,常数因子也较小。 ✅ 优势 :查找速度最快

    代价:为了维持这种"绝对平衡",插入或删除时可能需要多次旋转(甚至从叶子一路旋转到根),维护成本高;而且如果要范围查询的话需要中序遍历avl树

    然而,现实中极少存在"只读不写"的系统。一旦引入动态更新,AVL 树的劣势就显现出来了。

2.现实:需要频繁插入和删除(动态有序集合)

avl树要求的是绝对的平衡,左右子树高度差不能超过1,但是这种要求有的时候对实际的搜索太严格了,而且实际应用中很少有只搜索的,大部分都是搜索+动态增删

->红黑树

红黑树只保持近似平衡,它只是保证从根到任意叶子的最长路径不超过最短路径的两倍。虽然查找性能略慢于avl树,但是插入删除节点都更快,综合性能更好。所以在java的框架中很多地方都使用红黑树(hashmap等)

3.有范围查询的需求

avl和红黑树虽然能通过中序遍历实现范围查询,但是,遍历过程依赖递归或者栈,无法直接跳到下一个元素,缺乏显式的顺序连接,效率不高。

范围查询的最佳结构就是有序遍历+快速定位起点 ,通过在有序链表上建立索引的方式提升搜索的性能,一种类似于有序链表+搜索树的结构,这种结构的主要应用场景是在数据库中(通过键来搜索数据)

4.基于磁盘实现的数据库:

磁盘场景下的搜索情况与内存有明显差异,因为内存读取不同节点都是在内存中,速度很快,但是如果在磁盘中的话,加载不同的节点就非常慢了,所以这种情况下就需要在一个数据页中存放尽量多的数据,从而避免多次加载数据页

->b树和b+树

b树中每个节点即存放键也存放数据,这样会更快的找到数据,但是每个节点存放的数据就更少了(相较于只存放键来说),范围查询的话也不太方便

b+树则是每个非叶子节点只存放键,只在叶子节点中存放数据,并且由于叶子节点都是数据,所以可以形成一个链表,非常有利于范围查询;同时由于非叶子节点中只存放键,所以数据页中就可以放更多的键,从而只访问一个节点,就可以排除大量的键,树的高度也更低了,磁盘的访问次数也大大减小。**这也就是上文所提到的有序链表+搜索树的结构。**通过叶子节点实现有序链表,同时将上层的搜索树使用多叉树来减小节点数实现更少的磁盘访问次数。

可以说b+树是磁盘实现数据库下的最佳实践。

5.内存实现的数据库:

内存实现的数据库实际上是在内存和磁盘数据库中间起到一个缓冲的作用,所以一般来说这样的数据库对性能要求都比较高

但是对于有序链表+搜索树这种数据结构来说,一个节点不仅要维护自己作为搜索树的左右子树节点,还要维护自己作为链表的前后节点,所以上层的搜索树往往比较复杂

->跳表

快表通过上层仍然是有序链表的方式改进搜索树的这种复杂情况,相当于把搜索树的每个节点只保留右子树,通过多层这样的结构来保证搜索的效率(上层搜索粒度大,下层搜索粒度小)

这种数据结构不仅实现了搜索+范围查找的目的,而且避免了为了维护搜索树平衡所带来的消耗。

相关推荐
CSharp精选营1 天前
关系型 vs 非关系型:从原理到选型,一文搞定数据库核心分类
数据结构·nosql·关系型数据库·非关系型数据库·技术选型
刘马想放假4 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
北域码匠5 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
Darling噜啦啦12 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
小小工匠13 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
玖玥拾13 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
Qres82113 天前
算法复键——树状数组
数据结构·算法
牛油果子哥q13 天前
并查集(DSU)超精讲,路径压缩、按秩合并、万能模板、连通性判定、最小生成树与刷题实战全解
数据结构·c++·最小生成树·并查集
凌波粒13 天前
LeetCode--491.递增子序列(回溯算法)
数据结构·算法·leetcode
WL学习笔记14 天前
单项不带头不循环链表
数据结构·链表