索引介绍
MySQL索引是一种用于快速查询和检索数据的数据结构,其本质可以看作是一种排序好的数据结构,类似于书籍的目录。通过索引,数据库可以大幅减少需要扫描的数据量,直接定位到符合条件的记录,从而显著加快数据检索速度,减少磁盘I/O次数。
索引的分类:
根据底层存储方式和特性,MySQL索引主要可以分为以下几类:
- B-Tree 索引: MySQL中默认和最常用的索引类型,InnoDB和MyISAM存储引擎都使用B+树实现B-Tree索引。
- 哈希索引 (Hash Index): 类似键值对的形式,可以一次定位到数据,但仅适用于精确查找,不支持范围查询和排序。
- R-Tree 索引: 主要用于空间数据类型(如
geometry),优势在于范围查找,但效率相对较低。 - 全文索引 (Full-text Index): 用于在文本数据中进行关键词搜索。
根据索引结构和数据存放方式,索引又可以分为:
- 聚簇索引 (Clustered Index): 索引结构和数据一起存放的索引。在InnoDB中,主键索引就是聚簇索引。表的
.ibd文件包含了索引和数据,B+树的每个非叶子节点存储索引,叶子节点存储索引和对应的数据。 - 非聚簇索引 (Non-Clustered Index) / 二级索引 (Secondary Index): 索引结构和数据分开存放的索引。在MySQL的MyISAM引擎中,无论主键还是非主键,都使用非聚簇索引。在InnoDB中,二级索引的叶子节点存放的是主键值,需要根据主键再“回表”查询数据。
索引的优点:
- 提高查询速度: 索引可以帮助MySQL快速定位到需要查询的数据,减少磁盘I/O次数,从而显著加快数据检索速度。
- 保证数据唯一性: 通过创建唯一索引(Unique Index)或主键索引(Primary Key),可以确保表中的某一列(或几列组合)的值是独一无二的,防止重复数据。
- 优化排序和分组: 索引可以帮助MySQL快速进行排序和分组操作,提高查询效率。
- 减少硬盘I/O读取: 索引通过减少扫描全表数据的次数来减少硬盘I/O。
索引的缺点:
- 占用存储空间: 索引是一种用空间换时间的做法,需要占用额外的磁盘空间。
- 降低增删改效率: 每次对表进行增删改操作时,MySQL不仅要保存或更新数据,还需要维护对应的索引文件,这会降低表的DML(数据操作语言)效率。
- 维护成本: 维护索引需要消耗数据库资源。
- 不合理索引可能降低性能: 如果索引建立不合理或过多,反而可能导致查询性能下降。
- 对小表可能性能下降: 对于数据量很小的表,建立索引可能导致查询性能下降,因为查询优化器可能认为直接全表扫描更高效。
B+树详细介绍
B+树是数据库索引和文件系统中最常见的数据结构,广泛应用于MySQL等关系型数据库的索引实现中。 它是B-树的变种,在B-树的基础上进行了优化。
B+树的结构特点:
- 所有数据存储在叶子节点: B+树中,非叶子节点只存储键值(索引),不存储实际数据指针;所有的数据都存储在叶子节点中。
- 叶子节点之间通过指针连接: B+树的叶子节点之间通过双向链表相互连接,形成一个有序链表,便于顺序访问和范围查询。
- 非叶子节点冗余键值: 非叶子节点中的索引键值会在叶子节点中再次出现,并且非叶子节点有多少个子节点,就有多少个索引。
- 平衡树: B+树是平衡树,任何查询从根到叶子的路径长度都相同,这保证了查询性能的稳定性。
B+树的工作原理(以InnoDB聚簇索引为例):
当表有主键时,InnoDB会使用主键创建聚簇索引。聚簇索引的B+树的叶子节点存放的是完整的数据行。如果没有显式指定主键,InnoDB会自动选择一个唯一非空索引作为主键,如果都没有,则会自动创建一个6字节的自增主键。
当进行数据查找时,从根节点开始,根据要查找的键值与当前节点的键值进行比较,选择相应的子节点继续查找,直到找到叶子节点。因为叶子节点包含了所有数据,一旦到达叶子节点,就能获取到所需的数据。
对于非主键索引(二级索引),其B+树的叶子节点存储的是主键值而不是实际数据。因此,通过二级索引查找数据时,需要先在二级索引B+树中找到对应的主键值,然后根据主键值再到聚簇索引B+树中查找实际的数据行,这个过程称为“回表”。
B+树的优势:
- 更适合磁盘存储: 节点大小通常设置为磁盘页(Page)的大小(如4KB或16KB),一次磁盘I/O可以读取一个完整的节点。高扇出特性使得一次I/O能获取更多路径信息,有效减少I/O次数。
- 稳定的查询性能: 由于所有查询都必须到达叶子节点才能获取数据,且树的高度是平衡的,所以B+树的查询性能非常稳定,时间复杂度稳定在O(logmN)。
- 高效的范围查询和顺序扫描: 叶子节点之间通过链表连接,使得范围查询(如
WHERE id BETWEEN 5 AND 10)非常高效,只需找到起始叶子节点,然后沿着链表顺序遍历即可。 - 充分利用磁盘预读特性: 连续的叶子节点数据在磁盘上也是连续存放的,这有利于利用磁盘预读的特性,提高I/O效率。
- 更高的并发性: 非叶子节点只存储索引,不存储数据,减少了对数据行的锁定竞争,提高了并发访问能力。
B+树的劣势:
- 空间开销相对较大: 非叶子节点存储了冗余的键(作为索引),这些键在叶子节点中也存在,相比B树会有一定的空间浪费。
- 单点查询性能可能略低于B树(理论上): 在B树中,数据可能存储在非叶子节点,如果查询命中非叶子节点,则可以更快返回。但在B+树中,所有查询都必须到达叶子节点。然而,考虑到磁盘I/O是主要瓶颈,B+树在实际应用中通常表现更好。
- 更新代价: 插入和删除操作可能导致节点的分裂或合并,需要调整树的结构,从而引入额外的I/O和CPU开销。