数据库索引

索引是一种以存储空间换取查询时间的数据库优化手段 —— 知识点笔记

一、什么是索引

索引是数据库中对表中一列或多列的值进行排序的一种数据结构。它类似于一本书的目录,通过建立「键值 → 数据位置」的映射关系,帮助数据库快速定位目标数据,而不必逐行扫描整张表。

简单来说,索引是以存储空间换取查询时间的极大缩短。

二、索引的作用

  1. 加快数据检索的速度:通过减少查询过程中需要扫描的数据行数,将全表扫描(O(n))优化为近似 O(log n) 的树查找。
  2. 加快表连接和排序:索引本身是有序的,数据库可以直接利用索引的有序性完成排序,避免额外的文件排序(Filesort)操作。
  3. 保证数据的唯一性:通过创建唯一索引(UNIQUE),数据库可以强制保证某列(或多列)数据的唯一性。

三、索引的代价(缺点)

  1. 占用存储空间:每个索引都需要额外的磁盘空间来存储索引结构。
  2. 降低写入性能:对表进行 INSERT、UPDATE、DELETE 时,除了修改数据本身,还需要同步维护所有相关索引,写操作成本上升。
  3. 可能失效或误用:索引使用不当(如违反最左前缀、字段类型不匹配等)不仅无法加速,反而可能拖慢查询。
因此索引并非越多越好,需要根据实际查询场景权衡。

四、索引的原理

B+树(B+树是 B 树的一种变种)

  1. 分层的节点结构:B+树由根节点、内部节点和叶子节点构成。
  2. 内部节点:只记录存储索引键值和指向子节点的指针,不存储实际数据。
  3. 叶子节点:所有的数据都存储在叶子节点上,这些叶子节点通过双向链表连接,形成了一个有序的数据序列。
  4. 树高稳定:所有叶子节点都在同一层,这意味着查找任何数据都需要经历相同层数的路径,查询时间非常稳定。

为什么使用 B+ 树

  1. 大幅减少磁盘 I/O:磁盘 I/O 是数据库操作中最慢的环节。B+树通过其「矮胖」的结构,将查找数据所需的磁盘读取次数降到最低(通常 2-3 次)。每个节点的大小通常被设计为等于一个磁盘页(如 MySQL 的 16KB),使得一次 I/O 就能读取一个完整的节点。相比之下,二叉树等结构在数据量大时树高会急剧增加,导致 I/O 次数剧增。
  2. 卓越的范围查询能力:数据库的范围查询(如 BETWEEN、>、<)非常频繁。B+树的叶子节点通过链表串联,一旦定位到范围的起点,只需沿着链表顺序扫描即可,效率极高。而 B 树的数据分散在各处,进行范围查询需要进行复杂的中序遍历,效率低下。
  3. 查询性能稳定:在 B+树中,任何查找都必须从根节点走到叶子节点,因此所有查询的路径长度都相同,查询效率非常稳定。而在 B 树中,数据可能存储在任意节点,数据靠近根节点时查询快,靠近叶子节点时查询慢,性能不稳定。
  4. 更高的空间利用率:由于内部节点不存数据,可以存储更多的键值,这进一步降低了树的高度,也使得数据在磁盘上存储得更紧密,具有良好的空间局部性,能提高缓存命中率。

五、索引的分类

按物理存储方式(以 MySQL InnoDB 为例)

类型说明特点
聚集索引
Clustered
索引的叶子节点直接存储整行数据(数据即索引),主键索引就是聚集索引。 每张表只能有一个。
非聚集索引
Secondary
叶子节点存储的是主键值(而非数据本身),需要通过主键再查一次数据。 可以有多个,查询时可能发生「回表」。

按功能 / 约束分类

类型说明
主键索引(Primary Key)唯一且非空,InnoDB 中即聚集索引。
唯一索引(Unique)保证列值唯一,允许 NULL。
普通索引(Normal)最基本的索引,无任何约束。
联合索引(Composite)由多个列组合成的索引,遵循最左前缀原则。
全文索引(Fulltext)用于大文本的全文检索。
前缀索引对字符串的前 N 个字符建立索引,节省空间。

六、联合索引与最左前缀原则

联合索引由多列组成,例如 INDEX(a, b, c),内部排序规则是:先按 a 排序,a 相同再按 b 排序,b 相同再按 c 排序。

最左前缀原则:查询条件必须从联合索引的最左侧列开始,且不能跳过中间列,否则无法完全利用该索引。

例如对 INDEX(a, b, c):

七、索引失效的常见场景

  1. 对索引列使用函数或表达式运算,如 where year(create_time) = 2025
  2. 隐式类型转换,如字符串列与数字比较 where phone = 13800000000
  3. 使用 like 以 % 开头,如 where name like '%张三'
  4. 联合索引违反最左前缀原则
  5. 查询条件使用 OR 且存在非索引列
  6. 使用 !=、<>、not in 等可能不走索引
  7. 索引列参与计算,如 where age + 1 = 20

八、建索引的建议

  1. 为 WHERE、ORDER BY、GROUP BY、JOIN 中频繁使用的列建索引。
  2. 区分度高(选择性好)的列适合建索引,如主键、唯一字段。
  3. 联合索引要注意列的顺序,把区分度高、查询频繁的列放在最左。
  4. 避免在频繁更新的列上建过多索引。
  5. 尽量使用覆盖索引,减少回表。