Mysql中的索引的定义和种类
核心概念:索引是什么?
想象一下你有一本很厚的书,你想找到其中关于某个特定主题的内容。你有两种方法:
-
从头到尾翻阅整本书 :这就像数据库中的全表扫描 (Full Table Scan)。如果书很长,这个过程会非常慢。
-
查阅书末尾的索引 :索引列出了书中的重要词汇或主题,并指明了它们出现在书的哪一页。你只需要快速查找索引(索引本身通常按字母排序),找到你想找的主题,然后直接翻到对应的页码。这就像数据库中的使用索引。
在数据库中,索引也是一个类似的数据结构 ,它存储了表中的某个(或多个)列的值,并关联着这些值对应的行的物理位置或唯一标识。它的主要目的是 加快数据的检索速度。
索引的本质:
-
一个独立于表数据之外的数据结构。
-
存储了表中列的有序副本(或部分副本)。
-
包含了指向原始数据行的指针或引用。
有了索引,数据库系统在执行查询时,就可以先在索引结构中快速定位到符合条件的索引项,然后通过索引项找到对应的数据行,而不需要扫描整个表的数据文件。
为什么索引的种类感觉很多?
索引的"种类"多,是因为我们从不同的维度去划分和命名索引:
-
根据数据结构: 索引底层使用什么数据结构来组织数据(最常见的是 B-Tree/B+Tree)。
-
根据逻辑功能或约束: 索引除了加速查询,是否还具备其他功能或强制约束(例如,是否要求索引列的值唯一)。
-
根据索引和数据行的关联方式: 索引的数据和实际的表数据是如何存放和关联的(主要是 InnoDB 引擎中的概念,聚簇索引 vs 非聚簇索引/二级索引)。
理解了这几个维度,就能明白为什么会有各种不同的索引名称。
常见索引种类(举例):
我们主要以最常用的 InnoDB 存储引擎 来讲解,因为它对索引的处理方式(特别是聚簇索引)非常有代表性。
1. 根据数据结构:
-
B-Tree (B+Tree) 索引:
-
这是 MySQL 中最常见的索引类型,特别是 InnoDB 引擎的标准索引。
-
结构:一种平衡的多路搜索树。
-
节点(非叶子节点)存储索引键的范围和指向子节点的指针。
-
叶子节点存储完整的索引键,并包含指向实际数据行(对于二级索引)或直接包含数据行本身(对于聚簇索引)的指针。
-
B+Tree 特点: 所有的真实数据或指向数据的指针都只存在于叶子节点,并且叶子节点之间通过双向链表连接。这使得范围查询非常高效。
-
-
适用场景:等值查找、范围查找、排序(前缀匹配)。
-
例子: 你创建的大部分 PRIMARY KEY, UNIQUE, INDEX (普通索引) 默认都是 B+Tree 索引。
-
-
Hash 索引:
-
基于哈希表实现。
-
结构:将索引列的值进行哈希计算,得到一个哈希值,然后将这个哈希值映射到存储数据行指针的地址。
-
适用场景:只适用于等值查找 (=)。无法用于范围查找、排序或模糊匹配(如 LIKE 'prefix%')。
-
特点:查找速度理论上非常快 (O(1) 平均)。但在 MySQL 中,只有 MEMORY 存储引擎默认支持 Hash 索引,InnoDB 引擎虽然也支持一种称为 "Adaptive Hash Index" 的特性,但它是 InnoDB 内部根据工作负载自动创建和管理的,你无法手动创建 Hash 索引。
-
例子: MEMORY 表的 PRIMARY KEY 或 UNIQUE INDEX 默认使用 Hash 索引。你不能用 CREATE INDEX ... USING HASH 来创建 InnoDB 表的 Hash 索引。
-
-
Fulltext 索引 (全文索引):
-
用于在大文本字段 (如 TEXT, VARCHAR) 中进行关键词搜索。
-
结构:通常使用倒排索引 (Inverted Index)。记录了哪些词出现在了哪些文档(行)中。
-
适用场景:搜索文章、博客内容等包含大量文本的字段。
-
使用方式:使用 MATCH(column_name) AGAINST('keywords') 语法进行查询。
-
例子: 在存储文章内容的 TEXT 字段上创建 FULLTEXT INDEX。
ALTER TABLE articles ADD FULLTEXT INDEX idx_content (content); SELECT title FROM articles WHERE MATCH(content) AGAINST('MySQL Index');
-
-
Spatial 索引 (空间索引):
-
用于存储和检索地理空间数据 (如点、线、多边形)。
-
结构:通常使用 R-Tree。
-
适用场景:地理信息系统 (GIS) 应用,查找某个区域内的点,判断图形是否相交等。
-
使用方式:需要使用 MySQL 的空间数据类型 (GEOMETRY, POINT, POLYGON 等) 和空间函数。
-
例子: 在存储坐标的 POINT 字段上创建 SPATIAL INDEX。
CREATE TABLE cities ( name VARCHAR(255), location POINT, SPATIAL INDEX idx_location (location) ); -- 查询某个矩形区域内的城市 SELECT name FROM cities WHERE MBRContains(GeomFromText('POLYGON((...))'), location);
-
2. 根据逻辑功能或约束:
-
Primary Key (主键索引):
-
功能/约束:唯一标识表中的每一行。值必须是唯一的,并且不能为 NULL。
-
特殊性 (InnoDB):它不仅仅是一个索引,它也是 聚簇索引 (后面会解释)。
-
一个表最多只能有一个主键。
-
例子:
CREATE TABLE users ( user_id INT PRIMARY KEY, -- 创建主键索引 username VARCHAR(50) UNIQUE, ... );
-
-
Unique Index (唯一索引):
-
功能/约束:确保索引列中的所有值都是唯一的。允许包含 NULL 值(如果列定义允许 NULL 的话,多个 NULL 值是允许的,因为 NULL 不等于 NULL)。
-
一个表可以有多个唯一索引。
-
例子: 确保用户名或邮箱地址是唯一的。
ALTER TABLE users ADD UNIQUE INDEX idx_username (username); -- 创建唯一索引 ALTER TABLE users ADD UNIQUE INDEX idx_email (email); -- 创建另一个唯一索引
-
-
Standard Index / Non-Unique Index (普通索引 / 非唯一索引):
-
功能/约束:没有任何约束,唯一的作用就是提高查询性能。
-
索引列的值可以重复。
-
一个表可以有多个普通索引。
-
例子: 提高按加入日期查询或排序的速度。
CREATE INDEX idx_join_date ON users (join_date); -- 创建普通索引
-
-
Composite Index (复合索引 / 组合索引):
-
功能/约束:在多个列上创建一个索引。
-
查询时只有使用了索引的前缀(从左到右的列)才能有效地利用该索引。例如,在 (col1, col2, col3) 上创建的复合索引,可以用于查询 WHERE col1 = ...、WHERE col1 = ... AND col2 = ...、WHERE col1 = ... AND col2 = ... AND col3 = ...,但不能直接用于 WHERE col2 = ... 或 WHERE col3 = ...。
-
例子: 提高按用户名和邮箱联合查询的速度。
CREATE INDEX idx_username_email ON users (username, email); -- 创建复合索引
-
3. 根据索引和数据行的关联方式 (主要在 InnoDB 中):
这是理解 InnoDB 索引的关键。
-
Clustered Index (聚簇索引):
-
特点: 索引的叶子节点直接存储了表的所有行数据。
-
数据行是根据聚簇索引键的顺序物理地存储在磁盘上的。
-
一个表只能有一个聚簇索引。
-
在 InnoDB 中,聚簇索引通常是:
-
如果你定义了 PRIMARY KEY,那么它就是聚簇索引。
-
如果你没有定义 PRIMARY KEY,但定义了 UNIQUE NOT NULL 索引,那么第一个这样的索引会被选为聚簇索引。
-
如果上述两者都没有,InnoDB 会隐式地创建一个隐藏的聚簇索引(一个 6 字节的 ROW_ID)。
-
-
查询速度非常快,特别是按主键等值查询或范围查询,因为索引结构直接指向了完整的数据行。
-
插入数据时,需要根据主键顺序找到合适的物理位置进行插入,可能会导致页分裂。
-
-
Secondary Index (二级索引 / 非聚簇索引):
-
特点: 索引的叶子节点存储了索引键的值,以及指向对应聚簇索引键值的指针。
-
它不包含完整的数据行。
-
一个表可以有多个二级索引。
-
查询流程:先通过二级索引找到对应的聚簇索引键值,然后再通过聚簇索引键值去聚簇索引中查找完整的数据行 。这个过程称为回表 (Lookup)。
-
例子: 除了主键之外你创建的所有 UNIQUE, INDEX, FULLTEXT, SPATIAL 索引都是二级索引。
-
理解例子:
假设我们有 users 表:
CREATE TABLE users (
user_id INT PRIMARY KEY, -- 主键,同时是聚簇索引
username VARCHAR(50) UNIQUE, -- 唯一索引,二级索引
email VARCHAR(100), -- 普通字段
join_date DATE, -- 普通字段
status ENUM('active', 'inactive'), -- 普通字段
INDEX idx_join_date (join_date) -- 普通索引,二级索引
) ENGINE=InnoDB;
-
当你执行 SELECT * FROM users WHERE user_id = 100; 时,MySQL 使用聚簇索引,直接根据 user_id = 100 在聚簇索引 B+Tree 中找到对应的叶子节点,该叶子节点包含了 user_id = 100 的完整行数据。速度极快。
-
当你执行 SELECT * FROM users WHERE username = 'Alice'; 时,MySQL 使用 idx_username 二级索引。它会在 idx_username 的 B+Tree 中找到 username = 'Alice' 对应的叶子节点。这个叶子节点存储的是 'Alice' 和对应的 user_id 值(假设是 101)。然后,MySQL 会使用 user_id = 101 去聚簇索引中查找完整的行数据(这就是回表)。这个过程比直接通过主键查找多了一步。
-
当你执行 SELECT user_id, username FROM users WHERE username = 'Alice'; 时,MySQL 仍然使用 idx_username 二级索引。它找到 'Alice' 对应的 user_id (101)。因为查询结果只需要 user_id 和 username,而这两列的值都已经在二级索引的叶子节点中包含了(二级索引叶子节点包含索引列和主键列),所以此时不需要回表 。这种情况称为覆盖索引 (Covering Index),效率很高。
总结:
-
索引是用于加速查询的数据结构,最常见的是 B+Tree。
-
索引的种类繁多是因为从不同角度命名:底层结构、逻辑功能、数据关联方式。
-
在 InnoDB 中,核心是区分 聚簇索引 (数据本身就是索引的一部分,通常是主键) 和 二级索引 (单独的索引结构,叶子节点存主键,需要回表查找数据)。
-
选择和设计合适的索引是数据库性能优化的关键。需要考虑查询模式、写入频率、存储空间等因素。