要回答为什么 InnoDB(MySQL 的存储引擎) 使用 B+ 树而不是跳表(Skip List),以及为什么 Redis 使用跳表而不是 B+ 树,需要分析两者的数据结构特性、使用场景和设计目标。以下是详细的对比和原因分析。


1. InnoDB 为什么使用 B+ 树而不是跳表?

B+ 树的特点

  • 结构:B+ 树是一种多路平衡搜索树,非叶子节点存储键值,叶子节点存储完整数据(聚簇索引)或键值+指针(非聚簇索引),叶子节点通过双向链表连接。
  • 优势
    • 高效范围查询:叶子节点链表支持快速顺序访问,适合 WHERE id BETWEEN 10 AND 20 等查询。
    • 低树高:多路分支(每个节点存储多个键)减少树高,降低磁盘 I/O。
    • 磁盘优化:节点大小与 InnoDB 页面(默认 16KB)对齐,最大化 I/O 效率。
    • 并发支持:支持细粒度锁(如行锁、间隙锁)和 MVCC(多版本并发控制),适合事务性数据库。
  • 劣势
    • 键值重复存储,增加空间开销。
    • 插入/更新可能导致页面分裂,维护成本较高。

说明:

  • 在B+树中,非叶子节点存储键值(索引字段),而叶子节点存储完整的键值和数据(或数据指针)。这意味着同一个键值可能会在非叶子节点和叶子节点中重复出现,导致存储空间的冗余
  • 例如,在一个name字段的索引中,name值会在非叶子节点和叶子节点中都存储,增加了存储开销。

跳表的特点

  • 结构:跳表是一种基于链表的概率性数据结构,通过多层索引(随机层数)加速查找,类似平衡树的二分搜索。
  • 优势
    • 简单实现:相比 B+ 树,跳表实现更简单,代码复杂度低。
    • 动态更新:插入和删除操作效率较高,平均时间复杂度为 O(log n),无需复杂平衡操作。
    • 内存友好:跳表基于指针,适合内存操作,空间利用率较高。
  • 劣势
    • 概率性性能:跳表的性能依赖随机层分配,最坏情况退化为 O(n)。
    • 范围查询较弱:虽然支持范围查询,但需要逐节点遍历链表,效率不如 B+ 树的顺序链表。
    • 磁盘 I/O 不友好:跳表节点大小不固定,难以与磁盘页面对齐,增加 I/O 开销

为什么 InnoDB 选择 B+ 树?

  1. 磁盘 I/O 优化

    • InnoDB 是磁盘数据库,查询性能受限于磁盘 I/O。B+ 树的节点设计与页面大小对齐(16KB),每次 I/O 可读取多个键值,减少 I/O 次数。
    • 跳表的节点大小不固定,难以优化磁盘 I/O,可能导致更多随机读取,性能下降。
  2. 高效范围查询

    • 数据库常见操作包括范围查询(如 SELECT * FROM table WHERE id BETWEEN 10 AND 20)。B+ 树的叶子节点通过双向链表连接,支持高效顺序访问。
    • 跳表需要逐节点遍历,范围查询效率较低,尤其在数据量大时。
  3. 并发和事务支持

    • InnoDB 支持复杂的事务隔离级别(如可重复读)和 MVCC。B+ 树的结构便于实现行锁、间隙锁和版本控制,减少锁冲突。
    • 跳表的链表结构难以支持细粒度锁,MVCC 实现复杂,可能导致并发性能下降。
  4. 稳定性能

    • B+ 树是平衡树,查询时间复杂度稳定为 O(log n)。跳表的性能依赖随机层分配,最坏情况退化为 O(n),不适合对性能一致性要求高的数据库。
  5. 数据量和持久化

    • InnoDB 处理大规模数据(百万到亿级记录),B+ 树的低树高和顺序存储适合磁盘环境。
    • 跳表更适合内存数据库或较小数据集,难以应对大规模磁盘存储。

总结

InnoDB 选择 B+ 树是因为它在磁盘 I/O 优化范围查询效率并发支持稳定性能方面更适合关系型数据库的需求。跳表的概率性性能、较弱的范围查询支持和磁盘不友好特性使其不适合 InnoDB 的场景。


2. Redis 为什么使用跳表而不是 B+ 树?

Redis 的特点

  • Redis 是一个内存数据库,数据存储在内存中,I/O 延迟极低,查询性能主要受 CPU 和内存访问速度限制。
  • Redis 的有序集合(Sorted Set,ZSET)使用跳表作为主要数据结构,支持快速插入、删除、查找和范围查询。

跳表在 Redis 中的优势

  1. 内存操作优化

    • 跳表基于链表和指针,节点大小灵活,适合内存环境。内存访问速度快,跳表无需像 B+ 树那样优化磁盘页面大小。
    • B+ 树的节点设计(大节点、多键)针对磁盘 I/O,在内存中可能导致空间浪费和复杂维护。
  2. 简单实现

    • 跳表实现比 B+ 树简单,代码复杂度低,维护成本小。Redis 强调高性能和轻量级,跳表符合这一设计哲学。
    • B+ 树需要复杂的平衡操作(如节点分裂、合并),增加开发和维护成本。
  3. 动态更新效率

    • Redis 的 ZSET 频繁涉及插入和删除操作(如 ZADDZREM)。跳表的插入/删除平均时间复杂度为 O(log n),无需复杂平衡,适合高频更新。
    • B+ 树的插入/更新可能导致节点分裂或合并,成本较高,尤其在内存中无明显 I/O 优势。
  4. 范围查询支持

    • ZSET 常用于范围查询(如 ZRANGEBYSCORE)。跳表通过底层链表支持范围查询,虽然效率不如 B+ 树的双向链表,但在内存中差异较小。
    • Redis 数据量通常较小(内存限制),跳表足以满足范围查询需求。
  5. 空间效率

    • 跳表节点只存储必要指针和数据,空间利用率较高。B+ 树的键值重复存储(非叶子节点和叶子节点)在内存中可能浪费空间。
    • Redis 追求内存高效,跳表更适合。

B+ 树的劣势在 Redis 中

  1. 内存不友好

    • B+ 树节点设计为大块(如 16KB),在内存中可能导致空间浪费,且节点分裂/合并操作复杂。
    • 跳表的节点小且灵活,内存分配更高效。
  2. 复杂性

    • B+ 树需要维护多路平衡,代码复杂,不符合 Redis 轻量级设计。
    • 跳表通过随机层分配实现近似平衡,简单且性能足够。
  3. 无磁盘 I/O 需求

    • Redis 是内存数据库,I/O 成本几乎为零,B+ 树的磁盘优化优势(如低树高、页面对齐)无用武之地。
    • 跳表的 O(log n) 查找性能在内存中已足够快。
  4. 并发需求不同

    • Redis 是单线程模型,依赖事件驱动处理并发,无需复杂锁机制。跳表的简单结构适合单线程操作。
    • B+ 树在 InnoDB 中支持复杂的事务和锁机制,但在 Redis 的单线程环境中显得过于复杂。

Redis 中跳表的使用

  • Redis 的 ZSET 使用跳表存储元素及其分数(score),支持快速查找(ZSCORE)、排名(ZRANK)和范围查询(ZRANGEBYSCORE)。
  • 跳表结合哈希表(存储元素到分数的映射)实现 ZSET,提供高效的插入、删除和查询性能。
  • 示例:
    ZADD myzset 1 "user1" 2 "user2" 3 "user3"
    ZRANGEBYSCORE myzset 1 2
    • 跳表快速定位分数范围 [1, 2],通过链表遍历返回结果。

总结

Redis 选择跳表是因为它在内存环境中简单高效,支持动态更新、范围查询和排名操作,符合 Redis 轻量级、高性能的设计目标。B+ 树的磁盘优化和复杂平衡机制在内存数据库中无明显优势,且维护成本高。


3. B+ 树与跳表的对比总结

特性 B+ 树 跳表
结构 多路平衡树,叶子节点链表连接 多层链表,概率性平衡
查询复杂度 O(log n),稳定 O(log n) 平均,O(n) 最坏
范围查询 高效(双向链表) 较慢(逐节点遍历)
插入/删除 O(log n),需节点分裂/合并 O(log n),简单随机层分配
空间占用 键值重复,占用较多 灵活,空间效率较高
磁盘优化 节点与页面对齐,I/O 效率高 不适合磁盘,随机访问多
并发支持 支持细粒度锁、MVCC 简单结构,适合单线程
适用场景 磁盘数据库(InnoDB),大规模数据 内存数据库或较小数据集

InnoDB为什么不用跳表,Redis为什么不用B+树?的更多相关文章

  1. 聊聊Mysql索引和redis跳表 ---redis的有序集合zset数据结构底层采用了跳表原理 时间复杂度O(logn)(阿里)

    redis使用跳表不用B+数的原因是:redis是内存数据库,而B+树纯粹是为了mysql这种IO数据库准备的.B+树的每个节点的数量都是一个mysql分区页的大小(阿里面试) 还有个几个姊妹篇:介绍 ...

  2. redis为何单线程 效率还这么高 为何使用跳表不使用B+树做索引(阿里)

    如果想了解 redis 与Memcache的区别参考:Redis和Memcache的区别总结 阿里的面试官问问我为何redis 使用跳表做索引,却不是用B+树做索引 因为B+树的原理是 叶子节点存储数 ...

  3. 深入理解跳表及其在Redis中的应用

    前言 跳表可以达到和红黑树一样的时间复杂度 O(logN),且实现简单,Redis 中的有序集合对象的底层数据结构就使用了跳表.其作者威廉·普评价:跳跃链表是在很多应用中有可能替代平衡树的一种数据结构 ...

  4. Redis 为什么用跳表而不用平衡树

    Redis 为什么用跳表而不用平衡树? 本文是<Redis内部数据结构详解>系列的第六篇.在本文中,我们围绕一个Redis的内部数据结构--skiplist展开讨论. Redis里面使用s ...

  5. 【转】Redis为什么用跳表而不用平衡树?

    Redis里面使用skiplist是为了实现sorted set这种对外的数据结构.sorted set提供的操作非常丰富,可以满足非常多的应用场景.这也意味着,sorted set相对来说实现比较复 ...

  6. 跳表,Redis 为什么用跳表而不用平衡树?

    https://juejin.im/post/57fa935b0e3dd90057c50fbc 在 Redis 中,list 有两种存储方式:双链表(LinkedList)和压缩双链表(ziplist ...

  7. Redis中为什么使用跳表---------转自http://blog.csdn.net/u010412301/article/details/64923131

    最近在研究数据库的一些底层实现,百度的面试官问到了跳表,当时没有回答上来,在csdn上看到了这篇文章,感觉写的比较好,希望大家可以多多交流. Redis里面使用skiplist是为了实现sorted ...

  8. 数据多的时候为什么要使用redis而不用mysql?

    2018-06-28  136465569...  转自 庆亮trj21bc... 修改   微信 分享: Redis和MySQL的应用场景是不同的. 通常来说,没有说用Redis就不用MySQL的这 ...

  9. Redis源码研究--跳表

    -------------6月29日-------------------- 简单看了下跳表这一数据结构,理解起来很真实,效率可以和红黑树相比.我就喜欢这样的. typedef struct zski ...

  10. 聊聊Mysql索引和redis跳表

    摘要 面试时,交流有关mysql索引问题时,发现有些人能够涛涛不绝的说出B+树和B树,平衡二叉树的区别,却说不出B+树和hash索引的区别.这种一看就知道是死记硬背,没有理解索引的本质.本文旨在剖析这 ...

随机推荐

  1. 【附源码】C语言的学生管理系统完整实现方案

    以下是一个基于C语言的学生管理系统完整实现方案,结合了结构体.链表.文件存储.菜单驱动等核心技术,参考了多个开源项目与课程设计案例. 系统支持管理员/学生双角色权限.数据持久化存储及完整增删改查功能, ...

  2. 鸿蒙Next仓颉语言开发实战教程:消息列表

    大家好,今天要分享的是仓颉语言开发商城应用实战教程的消息列表页面. 这个页面的导航栏和之前有所不同,不过难度并没有增加,只是标题移到了左边,我们使用两端对齐方式就能实现,导航栏部分的具体代码如下: R ...

  3. Tauri2.0-DeepSeek电脑端Ai对话|tauri2+vite6+deepseek流式ai聊天系统

    重磅新作tauri2.0+vue3.5+deepseek+arco桌面客户端ai流式输出聊天对话系统. tauri2-vue3-deepseek:桌面端ai聊天对话,基于Tauri2.x+Vite6集 ...

  4. 东航MU5735空难事件总结与分析

    东航MU5735空难事件总结与分析 事件概述 日期:2022年3月21日 航班:东方航空MU5735(昆明长水机场→广州白云机场) 机型:波音737-800(注册号B-1791,机龄6.8年) 伤亡: ...

  5. 前端技术栈加持:用 SpreadJS 实现分权限管理

    引言 在现代前端开发中,数据表格的应用极为广泛,而分权限管理在许多业务场景下是必不可少的功能.例如在表格类填报需求中,不同等级的登录用户能填报的区域有所不同.SpreadJS 作为一款强大的前端表格控 ...

  6. Mac launchctl 自定义服务启动

    原文:https://ichochy.com/posts/20231128.html launchd launchd – 系统范围内的守护进程(LaunchDaemons)/代理程序(LaunchAg ...

  7. 超实用!SpringAI提示词的4种神级用法

    提示词(Prompt)是输入给大模型(LLM)的文本指令,用于明确地告诉大模型你想要解决的问题或完成的任务,也是大语言模型理解用户需求并生成准确答案的基础.因此 prompt 使用的好坏,直接决定了大 ...

  8. windows10 搭建gitea服务器

    前一章写了在win上搭建gitlab服务器,因为gitlab服务器没有win的安装,所有需要在win上先按照lunix虚拟机. 这里有个小点的git服务器---gitea.适用于个人或者小团队所有. ...

  9. sublime text 2 snippet 设置

    1.标准文件写法 <snippet> <content><![CDATA[ 你需要插入的代码片段${1:name} ]]></content> < ...

  10. C# WinFomr 组合快捷键

    private void 控件名称_KeyDown(object sender, KeyEventArgs e) { //如果只是按了回车,而不是按组合快捷键就执行 if (e.KeyCode == ...