SQL B+ 树索引的核心思想
发布时间 - 2026-01-24 00:00:00 点击率:次B+树是专为磁盘I/O优化的多叉树结构,非叶子节点仅存键值和指针以降低树高,所有数据存储在叶子层且通过双向链表连接,支持高效范围查询与顺序扫描;其联合索引依赖最左前缀原则,且索引失效源于破坏键值有序性的操作。
为什么B+树不是B树,也不是二叉树
B+树是专为磁盘I/O优化的结构,不是为了内存查找快而设计的。它放弃“每个节点存数据”的直觉,转而让非叶子节点只存键值和子节点指针,这样一页(如InnoDB默认16KB)能塞下更多分支,树高自然压得更低——千万级数据通常只要3~4层,意味着最多4次磁盘读就能定位到记录。
- 二叉树在数据库
里基本不用:树太高,一次查询可能要几十次磁盘IO
- 平衡二叉树(AVL)仍只有2叉,空间利用率低,页内键值数少,树高难压缩
- B树虽支持多叉,但非叶子节点也存数据,挤占了本可用于扩大扇出的存储空间
- B+树把所有数据“沉”到叶子层,非叶子层纯粹做导航,这是它矮胖、抗写、适合范围查询的根本原因
叶子节点连成双向链表到底有什么用
这个设计不是锦上添花,而是解决真实场景问题的关键:比如SELECT * FROM orders WHERE create_time BETWEEN '2025-01-01' AND '2025-01-31',没有链表就得反复回树顶找下一个范围起点;有了next指针,查到第一个匹配叶子节点后,直接顺序往后扫,全程不跳回非叶子层。
- 范围查询(
BETWEEN、>=、LIKE 'abc%')性能跃升,避免多次随机IO -
ORDER BY+LIMIT场景天然受益,例如翻页查询ORDER BY id LIMIT 10000,20 - 聚簇索引下,叶子节点存的是完整行数据,链表即物理顺序,极大减少磁盘寻道
- 注意:二级索引叶子存的是
主键值而非整行,所以范围扫描后还需回表——这就是覆盖索引要解决的问题
联合索引为什么必须遵守最左前缀原则
因为B+树的搜索路径是单向递进的:从根节点开始,每一层都只按当前层级的键做二分查找,无法跳过某一层去“猜”下一层该比谁。联合索引(a, b, c)在内存中构建的是一棵以a为第一排序维度、b为第二、c为第三的树,a不等价于“过滤条件可选”,而是搜索入口的强制门槛。
- 支持:
WHERE a = ?、WHERE a = ? AND b > ?、WHERE a = ? AND b = ? AND c IN (?,?) - 不支持:
WHERE b = ?(没a,根本进不了树的第二层)、WHERE a > ? AND c = ?(c在第三层,但第二层b未定界,无法确定c的有序区间) - 隐式陷阱:
WHERE a = ? AND c = ?看似用了两个字段,实际只能用上a,c被跳过,变成索引扫描而非索引查找
索引失效的三个典型瞬间
不是建了索引就万事大吉。B+树依赖“值的有序性”工作,任何破坏这种有序性的操作都会让引擎弃用索引,退化为全表扫描。
-
WHERE name LIKE '%john':前导通配符导致无法利用B+树叶节点的有序链表,必须逐行匹配 -
WHERE YEAR(create_time) = 2025:函数计算使索引列create_time原始值不可见,优化器无法将查询条件映射到树中键值区间 -
WHERE user_id = '123'(user_id是INT类型):隐式类型转换触发全表扫描,MySQL会把每行user_id转成字符串再比,索引失效
这些不是配置问题,也不是版本bug,而是B+树结构本身决定的硬约束——它只认原始、有序、未加工的键值序列。
# mysql
# 隐式类型转换
# 为什么
# 2025
# sql
# select
# 字符串
# int
# 指针
# 类型转换
# 数据库
# bug
# 键值
# 的是
# 链表
# 树高
# 而非
# 专为
# 二叉树
# 跳过
# 第二层
# 这是
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?
原生JS实现图片轮播切换效果
Linux虚拟化技术教程_KVMQEMU虚拟机安装与调优
Laravel Docker环境搭建教程_Laravel Sail使用指南
在线制作视频的网站有哪些,电脑如何制作视频短片?
猎豹浏览器开发者工具怎么打开 猎豹浏览器F12调试工具使用【前端必备】
Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程
Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】
再谈Python中的字符串与字符编码(推荐)
laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程
Laravel如何处理JSON字段的查询和更新_Laravel JSON列操作与查询技巧
Win11怎么设置虚拟桌面 Win11新建多桌面切换操作【技巧】
如何用VPS主机快速搭建个人网站?
Laravel如何配置.env文件管理环境变量_Laravel环境变量使用与安全管理
Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】
通义万相免费版怎么用_通义万相免费版使用方法详细指南【教程】
如何用PHP快速搭建高效网站?分步指南
原生JS获取元素集合的子元素宽度实例
实例解析angularjs的filter过滤器
Python3.6正式版新特性预览
如何快速搭建虚拟主机网站?新手必看指南
PHP 实现电台节目表的智能时间匹配与今日/明日轮播逻辑
如何快速建站并高效导出源代码?
php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】
桂林网站制作公司有哪些,桂林马拉松怎么报名?
EditPlus 正则表达式 实战(3)
Android滚轮选择时间控件使用详解
JavaScript 输出显示内容(document.write、alert、innerHTML、console.log)
Laravel如何与Docker(Sail)协同开发?(环境搭建教程)
详解Huffman编码算法之Java实现
Android中AutoCompleteTextView自动提示
JavaScript数据类型有哪些_如何准确判断一个变量的类型
如何在万网开始建站?分步指南解析
JavaScript模板引擎Template.js使用详解
Laravel怎么进行数据库回滚_Laravel Migration数据库版本控制与回滚操作
Laravel如何与Vue.js集成_Laravel + Vue前后端分离项目搭建指南
Laravel怎么自定义错误页面_Laravel修改404和500页面模板
Laravel怎么实现模型属性的自动加密
iOS正则表达式验证手机号、邮箱、身份证号等
Laravel如何发送邮件和通知_Laravel邮件与通知系统发送步骤
Laravel Seeder怎么填充数据_Laravel数据库填充器的使用方法与技巧
Android okhttputils现在进度显示实例代码
如何快速登录WAP自助建站平台?
如何快速选择适合个人网站的云服务器配置?
电视网站制作tvbox接口,云海电视怎样自定义添加电视源?
javascript如何操作浏览器历史记录_怎样实现无刷新导航
如何快速搭建高效香港服务器网站?
laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法
如何选择可靠的免备案建站服务器?
中国移动官方网站首页入口 中国移动官网网页登录
下一篇:创建git怎么提交分支
下一篇:创建git怎么提交分支


