索引分类
按数据结构分类
B-Tree索引(B+Tree)
描述:默认的索引类型,大多数存储引擎(如InnoDB、MyISAM)支持。实际使用B+Tree结构,数据存储在叶子节点,叶子节点通过指针连接,支持高效的范围查询和排序。
适用场景:全值匹配、范围查询(如>、<、BETWEEN)、前缀匹配(最左前缀原则)等。
限制:无法跳过联合索引的左前列进行查询。
哈希索引
描述:基于哈希表实现,仅支持精确匹配查询(如=、IN)。Memory引擎显式支持,InnoDB通过自适应哈希索引(内部自动管理)优化等值查询。
适用场景:等值查询,如键值对存储。
限制:不支持范围查询、排序,哈希冲突可能影响性能。
全文索引
描述:倒排索引结构,用于文本内容的全文搜索(MATCH AGAINST)。MyISAM和InnoDB(5.6+)支持。
适用场景:文本字段的关键词搜索(如文章内容检索)。
限制:需显式创建,且对中文支持需借助插件(如ngram)。
空间索引(R-Tree)
描述:用于地理空间数据(如GEOMETRY、POINT类型),支持空间操作(如ST_Contains)。MyISAM和InnoDB(5.7+)支持。
适用场景:地理数据查询(如“查找某区域内的所有坐标”)。
按存储方式分类
聚簇索引(Clustered Index)
实现原理
数据结构:
B+Tree,叶子节点存储完整的数据行(数据页)。
存储方式:
数据按主键顺序物理存储,主键索引即数据本身。
默认行为:
InnoDB中,若未显式定义主键,会自动选择唯一非空列作为聚簇索引;若无则隐式创建ROWID。
优点
高效查询:范围查询或排序时,因数据物理连续,减少磁盘I/O。
避免回表:直接通过索引获取数据,无需二次查找。
缺点
插入性能依赖主键顺序:若主键非自增,可能导致页分裂和碎片化。
更新代价高:主键变更时,需调整数据物理位置,影响所有二级索引。
适用场景
频繁范围查询(如BETWEEN、ORDER BY)。
主键查询(如WHERE id = 1)。
二级索引(Secondary Index,非聚簇索引)
实现原理
数据结构:B+Tree,叶子节点存储主键值(InnoDB)或数据物理地址(MyISAM)。
存储方式:索引与数据分离,索引仅存储定位数据的必要信息。
分类
InnoDB二级索引:
叶子节点存储主键值,查询时需回表(通过主键到聚簇索引获取数据)。
覆盖索引优化:若查询字段均在索引中,无需回表(如SELECT a FROM t WHERE b=1,索引为(b, a))。
MyISAM索引:
叶子节点存储数据文件物理地址,直接定位数据。
优点
灵活创建:支持多列联合索引,适应复杂查询条件。
更新代价低:数据物理位置变化时,仅MyISAM需更新索引地址,InnoDB二级索引不受影响(因存储主键值)。
缺点
回表开销:InnoDB中若未覆盖索引,需二次查询聚簇索引,增加I/O。
范围查询效率低:非连续数据需多次磁盘寻址。
适用场景
高频等值查询(如WHERE email = ‘user@example.com’)。
覆盖索引优化查询(避免回表)。
聚簇索引与非聚簇索引核心区别
特性 | 聚簇索引 | 非聚簇索引 |
---|---|---|
数据存储方式 | 索引的叶子节点直接存储数据行(数据即索引) | 索引的叶子节点存储主键值或数据行的物理地址 |
索引与数据关系 | 索引和数据物理上连续存储 | 索引和数据物理上分离,索引指向数据位置 |
数量限制 | 每表仅一个聚簇索引 | 每表可创建多个非聚簇索引 |
回表查询 | 无需回表 | 需通过主键回表查询(InnoDB)或直接访问数据(MyISAM) |
适用引擎 | InnoDB | InnoDB的二级索引、MyISAM的所有索引 |
聚簇索引与非聚簇索引不同存储引擎的对比
引擎 | 索引类型 | 数据存储 | 二级索引叶子节点内容 |
---|---|---|---|
InnoDB | 聚簇索引(主键) | 数据按主键顺序存储 | 主键值(用于回表查询) |
非聚簇索引(二级) | 独立于数据存储 | ||
MyISAM | 非聚簇索引(所有) | 数据按插入顺序存储,索引与数据分离 | 数据文件物理地址(直接定位数据) |
按逻辑功能分类
主键索引(Primary Key)
描述:
唯一标识数据行,不允许NULL值。InnoDB中作为聚簇索引。
特点:
一个表仅一个主键,通常与AUTO_INCREMENT联用。
核心规则:
唯一性:主键列的值必须唯一,不能重复。
非空约束:主键列不允许 NULL 值。
数量限制:每个表只能有一个主键(但主键可以是多列联合)。
存储引擎差异:
InnoDB:主键索引是聚簇索引,数据按主键顺序存储。
MyISAM:主键索引是非聚簇索引,数据按插入顺序存储。
-- 新建表时定义主键索引
-- 基本语法
CREATE TABLE 表名 (列名 数据类型 PRIMARY KEY, -- 单列主键...
);
-- 示例:单列主键(自增)
CREATE TABLE user (id INT AUTO_INCREMENT PRIMARY KEY, -- 主键自增name VARCHAR(50),email VARCHAR(100)
);
-- 示例:联合主键(多列组合)
CREATE TABLE order_item (order_id INT,product_id INT,quantity INT,PRIMARY KEY (order_id, product_id) -- 联合主键
);-- 为已有表添加主键索引
-- 基本语法
ALTER TABLE 表名
ADD PRIMARY KEY (列名); -- 单列主键
ALTER TABLE 表名
ADD PRIMARY KEY (列1, 列2); -- 联合主键--主键索引的删除
-- 删除主键索引(不会删除列)
ALTER TABLE 表名 DROP PRIMARY KEY;
唯一索引(Unique Index)
描述:确保列值唯一,允许单个NULL值。可作用于单列或多列(联合唯一索引)。
特点:用于避免数据重复(如用户邮箱)。
-- 新建表时定义唯一索引
-- 单列唯一索引
CREATE TABLE 表名 (列名 数据类型,...UNIQUE [索引名] (列名) -- 索引名可选,默认与列名相同
);
-- 示例:用户表(邮箱唯一)
CREATE TABLE user (id INT AUTO_INCREMENT PRIMARY KEY,email VARCHAR(100) UNIQUE, -- 直接定义唯一索引name VARCHAR(50)
);
-- 多列联合唯一索引
CREATE TABLE order_item (order_id INT,product_id INT,quantity INT,UNIQUE idx_order_product (order_id, product_id) -- 自定义索引名
);-- 为已有表添加唯一索引
-- 基本语法
ALTER TABLE 表名
ADD UNIQUE [索引名] (列名); -- 单列唯一索引
ALTER TABLE 表名
ADD UNIQUE [索引名] (列1, 列2); -- 联合唯一索引-- 删除唯一索引
-- 语法
ALTER TABLE 表名 DROP INDEX 索引名;
普通索引(Secondary Index)
描述:基本的非唯一索引,无数据唯一性约束。
特点:仅加速查询,允许重复值和NULL。
-- 新建表时定义普通索引
-- 基本语法
CREATE TABLE 表名 (列名 数据类型,...INDEX [索引名] (列名) -- 单列普通索引-- 或INDEX [索引名] (列1, 列2) -- 联合索引
);-- 为已有表添加普通索引
-- 语法1:ALTER TABLE
ALTER TABLE 表名
ADD INDEX [索引名] (列名); -- 单列索引
ALTER TABLE 表名
ADD INDEX [索引名] (列1, 列2); -- 联合索引
-- 语法2:CREATE INDEX(效果与 ALTER TABLE 相同)
CREATE INDEX 索引名 ON 表名 (列名);-- 删除普通索引
-- 语法1:ALTER TABLE
ALTER TABLE 表名
DROP INDEX 索引名;
-- 语法2:DROP INDEX(效果相同)
DROP INDEX 索引名 ON 表名;
全文索引(Full-Text Index)
全文索引概述
全文索引(Full-Text Index) 是一种专为文本字段设计的索引类型,支持自然语言搜索和布尔搜索,能够快速定位文本中的关键词或短语。与普通索引(B-Tree)不同,全文索引通过分词技术将文本拆解为单词或词组,建立倒排索引结构,适用于模糊查询和语义匹配。
全文索引概核心特性
支持数据类型:CHAR、VARCHAR、TEXT。
存储引擎支持:
MyISAM:全版本支持。
InnoDB:MySQL 5.6+ 支持。
分词机制:默认按空格和标点分词(适用于英文),中文需借助插件(如ngram)。
搜索模式:
自然语言模式(NATURAL LANGUAGE MODE):按相关性排序结果。
布尔模式(BOOLEAN MODE):支持逻辑操作符(+, -, *)。
全文索引操作
创建全文索引
-- 建表时定义
CREATE TABLE articles (id INT AUTO_INCREMENT PRIMARY KEY,title VARCHAR(200),content TEXT,FULLTEXT INDEX ft_idx_content (content) -- 单列全文索引-- FULLTEXT INDEX ft_idx_title_content (title, content) -- 多列联合全文索引
) ENGINE=InnoDB;-- 为已有表添加
ALTER TABLE articles
ADD FULLTEXT INDEX ft_idx_content (content);
-- 或使用 CREATE INDEX
CREATE FULLTEXT INDEX ft_idx_title_content
ON articles (title, content);
删除全文索引
ALTER TABLE articles
DROP INDEX ft_idx_content;
-- 或使用 DROP INDEX
DROP INDEX ft_idx_title_content ON articles;
全文索引查询语法
-- 自然语言模式(默认)
SELECT * FROM articles
WHERE MATCH(content) AGAINST('database optimization');
-- 布尔模式(支持操作符)
SELECT * FROM articles
WHERE MATCH(content) AGAINST('+MySQL -Oracle' IN BOOLEAN MODE);
空间索引(Spatial Index)
空间索引概述
空间索引(Spatial Index) 是专为地理空间数据(如点、线、面)设计的索引类型,支持高效的空间关系查询(如相交、包含、距离计算)。MySQL 使用 R-Tree(R树)数据结构实现空间索引,适用于地理信息系统(GIS)、地图服务等场景。
空间索引核心特性
支持数据类型:
GEOMETRY、POINT、LINESTRING、POLYGON、MULTIPOINT 等。
存储引擎支持:
MyISAM:全版本支持。
InnoDB:MySQL 5.7.5+ 支持。
索引结构:R-Tree(平衡树结构,优化范围查询)。
空间函数支持:
ST_Contains(g1, g2):判断几何对象是否包含另一个。
ST_Distance(g1, g2):计算两个几何对象的最小距离。
ST_Intersects(g1, g2):判断几何对象是否相交。
空间索引核心操作
创建空间索引
-- 建表时定义
CREATE TABLE locations (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(100),position POINT NOT NULL SRID 4326, -- 指定坐标系(WGS84)SPATIAL INDEX idx_position (position) -- 创建空间索引
) ENGINE=InnoDB;-- 为已有表添加
ALTER TABLE locations
ADD SPATIAL INDEX idx_position (position);
-- 或使用 CREATE INDEX
CREATE SPATIAL INDEX idx_position
ON locations (position);
删除空间索引
ALTER TABLE locations
DROP INDEX idx_position;
-- 或使用 DROP INDEX
DROP INDEX idx_position ON locations;
空间索引查询示例
插入空间数据
-- 插入一个点(经度 116.4, 纬度 39.9)
INSERT INTO locations (name, position)
VALUES ('Beijing', ST_GeomFromText('POINT(116.4 39.9)', 4326));
查询示例
-- 范围查询(查找某矩形区域内的点)
SELECT * FROM locations
WHERE ST_Contains(ST_GeomFromText('POLYGON((116.3 39.8, 116.5 39.8, 116.5 40.0, 116.3 40.0, 116.3 39.8))', 4326),position
);-- 距离查询(查找距离某点 10 公里内的点)
SET @center = ST_GeomFromText('POINT(116.4 39.9)', 4326);
SELECT name,ST_Distance(@center, position) * 111195 AS distance_meters -- 转换为米(假设为WGS84)
FROM locations
WHERE ST_Distance(@center, position) <= 10 / 111195; -- 10公里
按应用场景分类
联合索引(Composite Index)
联合索引定义
联合索引(Composite Index) 是将多个列组合在一起创建的索引。例如,在用户表中,若经常需要根据城市和年龄进行查询,可以创建一个联合索引(城市, 年龄)。与单列索引不同,联合索引的键值是多个列值的组合,并按顺序存储在索引结构中(如B+树)。
联合索引核心原理
存储结构:
联合索引的键值由多个列的值按顺序拼接而成。例如,索引(A, B)的键值为(A1, B1)、(A1, B2)、(A2, B1)等。
在B+树中,键值按字典序排序:先按第一个列排序,第一个列相同再按第二个列排序,依此类推。
最左前缀原则(Leftmost Prefix Rule):
查询条件必须包含联合索引的最左侧列,才能有效使用索引。
有效场景:
WHERE A = 1
WHERE A = 1 AND B = 2
WHERE A = 1 AND B = 2 AND C = 3
无效场景:
WHERE B = 2(缺少最左列A)
WHERE B = 2 AND C = 3
联合索引的设计策略
高频查询优先:
将最常用的查询条件列放在索引左侧。
示例:若查询多为WHERE city = ‘X’ AND age > Y,则索引应为(city, age)而非(age, city)。
选择性高的列在前:
高选择性(唯一值多)的列应放在左侧,能更快缩小查询范围。
示例:性别(低选择性)和城市(高选择性)中,优先将城市放在左侧。
避免冗余索引:
若已存在索引(A, B),则索引(A)是冗余的,可删除。
控制索引数量:
每个索引会增加插入、更新、删除的开销,需权衡读写性能。
联合索引的局限性
无法跳过最左列:
若无最左列的查询条件,索引无法生效。
优化方案:单独为右侧列创建索引。
索引大小增加:
联合索引的存储空间随列数和列类型增长,尤其是文本类型列。
写入性能影响:
维护索引需要额外的I/O和计算资源,影响写入速度。
覆盖索引(Covering Index)
覆盖索引定义
覆盖索引(Covering Index) 是指某个索引包含了查询所需的所有字段,使得数据库无需访问数据行(无需回表)即可完成查询。覆盖索引的本质是利用索引结构本身存储查询所需的字段值,从而显著减少磁盘 I/O 和 CPU 计算开销。
覆盖索引的核心原理
索引结构存储数据:
在 B+Tree 索引中,叶子节点存储索引列的值。若查询的字段全部在索引中,直接读取索引即可返回结果。
示例(查询字段 city 和 age 均包含在索引 idx_city_age 中,无需回表查询数据行):
-- 表结构:id(主键),name,age,city
-- 索引:idx_city_age (city, age)
SELECT city, age FROM users WHERE city = 'Beijing';
避免回表(Key Lookup):
回表:普通索引的叶子节点存储主键值,需通过主键回表查询数据行。
覆盖索引:直接返回索引中的字段值,跳过回表步骤。
前缀索引(Prefix Index)
前缀索引的定义
前缀索引(Prefix Index) 是一种针对文本类型列(如 CHAR、VARCHAR、TEXT)的优化索引方法,通过仅对列值的前 N 个字符 建立索引,减少索引存储空间,同时平衡查询效率。
核心思想:用部分字符代替完整内容作为索引键值,牺牲一定区分度以换取更小的索引体积。
前缀索引的适用场景
超长文本字段:
例如 VARCHAR(255) 的邮箱地址、URL 等。
前缀区分度高:
字段的前几个字符已足够区分不同数据(如订单号前缀)。
存储空间敏感:
需减少索引占用空间(尤其对大数据表)。
如何创建前缀索引
-- 对单列的前 N 个字符创建索引
CREATE INDEX 索引名 ON 表名 (列名(N));-- 示例:对 name 列的前 10 个字符创建索引
CREATE INDEX idx_name_prefix ON users (name(10));-- 联合索引中可指定不同列的前缀长度
CREATE INDEX idx_name_email ON users (name(5), email(20));
前缀长度的选择
选择合适的前缀长度是核心优化点,需满足 高选择性(不同值占比高)且 长度最小化
计算选择性
-- 计算不同前缀长度的选择性
SELECT COUNT(DISTINCT LEFT(列名, 10)) / COUNT(*) AS sel10,COUNT(DISTINCT LEFT(列名, 20)) / COUNT(*) AS sel20,COUNT(DISTINCT LEFT(列名, 30)) / COUNT(*) AS sel30
FROM 表名;
结果解读
sel10=0.85:前 10 字符能区分 85% 的数据。
若 sel20=0.86,则长度 10 到 20 的提升不大,选择 10 即可。
经验法则
确保前缀长度的选择性接近完整列的选择性。
例如:若完整列选择性为 95%,则前缀长度应使选择性 ≥ 90%。
前缀索引的优缺点
其他索引类型
自适应哈希索引(Adaptive Hash Index)
InnoDB自动为频繁访问的索引页创建哈希索引,加速等值查询,无需用户干预。
降序索引(Descending Index)
MySQL 8.0+支持,针对ORDER BY column DESC优化,提升排序性能。
是否对字段建立索引
适合建立索引的字段
- 高频查询条件(WHERE 子句中的字段)
- 主键(Primary Key)(主键默认创建聚簇索引(InnoDB)或唯一索引(MyISAM))
- 外键(Foreign Key)(外键字段常用于表连接(JOIN),索引可加速连接性能)
- 排序和分组字段(ORDER BY / GROUP BY)(频繁用于排序或分组的列)
- 高选择性字段(区分度高的列)
列中不同值的比例高(接近唯一):身份证号(唯一)比性别(只有2-3种值)更适合索引 - 联合查询字段(联合索引)
- 覆盖索引(Covering Index)
不适合建立索引的字段
- 低选择性字段
示例:
性别(只有 “男/女”)、布尔状态(0/1)。
索引可能比全表扫描更慢(优化器可能忽略索引)。 - 频繁更新的字段
代价:索引维护成本高,影响写入性能。
示例:
实时更新的计数器字段(如 view_count)。 - 大文本或二进制字段(TEXT/BLOB)
问题:索引体积大,效率低。
替代方案:
使用前缀索引(VARCHAR(255) 前20字符)。
计算哈希值并索引哈希列。 - 小表的字段
规则:数据量小的表(如配置表)无需索引。
原因:全表扫描可能更快。
索引失效的场景
违反最左前缀原则(联合索引)
场景:
联合索引 (A, B, C),但查询条件中未包含最左列 A。
示例:
SELECT * FROM table WHERE B = 2; -- 无法使用索引
SELECT * FROM table WHERE B = 2 AND C = 3; -- 无法使用索引
原因:B+Tree 索引按 A → B → C 顺序构建,缺失最左列无法定位索引范围。
优化方案:
查询条件需包含最左列。
单独为 B 或 C 建立索引。
对索引列进行运算或函数操作
场景:对索引字段进行运算或函数处理后作为查询条件。
示例:
SELECT * FROM users WHERE YEAR(create_time) = 2023; -- 索引失效
SELECT * FROM users WHERE id + 1 = 100; -- 索引失效
原因:运算后的值无法匹配索引中存储的原始值。
优化方案:
– 避免函数,改为范围查询
SELECT * FROM users
WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31';-- 直接使用原始值计算
SELECT * FROM users WHERE id = 100 - 1;
使用模糊查询 LIKE 以通配符开头
场景:LIKE 查询以 % 开头。
示例:
SELECT * FROM users WHERE name LIKE '%Alice%'; -- 无法使用索引
SELECT * FROM users WHERE name LIKE 'Alice%'; -- 可使用索引
原因:前缀模糊匹配破坏了索引值的顺序结构。
优化方案:
尽可能使用右侧模糊(如 ‘Alice%’)。
使用全文索引(FULLTEXT)替代 LIKE。
隐式类型转换(字段类型不同)
场景:字段类型与查询值类型不一致。
示例:
– 假设 phone 字段为 VARCHAR,但查询时使用数字
SELECT * FROM users WHERE phone = 13812345678; -- 索引失效
原因:数据库需将字段隐式转换为数值类型,导致无法匹配索引。
优化方案:严格匹配字段类型:
SELECT * FROM users WHERE phone = '13812345678';
使用 OR 连接非索引字段
场景:OR 连接的多个条件中存在未索引字段。
示例:
-- name 有索引,age 无索引
SELECT * FROM users WHERE name = 'Alice' OR age = 25; -- 全表扫描
原因:优化器无法通过单一的索引覆盖所有条件。
优化方案:
为 age 字段添加索引。
拆分成两个查询后用 UNION 合并。
联合索引范围查询后的列失效
场景:联合索引 (A, B, C),查询条件中存在 A 的范围查询,后续条件无法使用索引。
示例
SELECT * FROM table
WHERE A > 100 AND B = 2; -- B 无法使用索引过滤
原因:B+Tree 的索引结构在范围查询后,后续字段无序,无法快速定位。
优化方案:
调整索引顺序:将等值查询的字段放在范围字段前。
单独为 B 建立索引。
数据区分度过低(低选择性)
场景:字段值重复率过高(如性别、状态码)。
示例:
-- gender 字段仅有 'M'/'F' 两个值
SELECT * FROM users WHERE gender = 'M'; -- 索引可能被优化器忽略
原因:索引扫描可能比全表扫描更慢(需回表多次)。
优化方案:
避免为此类字段单独建索引。
与其他高选择性字段组成联合索引。
使用 != 或 NOT IN或NOT EXISTS
场景:查询条件中包含否定操作符。
示例:
SELECT * FROM users WHERE status != 1; -- 索引失效
SELECT * FROM users WHERE id NOT IN (1, 2, 3); -- 索引失效
原因:否定操作符需要扫描大部分数据,优化器倾向于全表扫描。
优化方案:重写查询逻辑或限制查询范围。
列比对、表达式比对
当查询条件中对两列进行直接比较(如 col1 = col2、col1 > col2、col1 + col2 > 100)时,优化器无法直接利用单列索引,通常会导致全表扫描或低效的索引使用。其根本原因是索引结构的设计针对单列值的存储和查找,而非列之间的动态关系。
排查索引是否失效的工具
使用 EXPLAIN 分析查询执行计划:
EXPLAIN SELECT * FROM users WHERE name = 'Alice';
关键字段解读:
type:
ref:使用索引查询。
index:全索引扫描(需检查是否覆盖索引)。
ALL:全表扫描(可能索引失效)。
key:实际使用的索引。
Extra:
Using index:覆盖索引有效。
Using filesort:未使用索引排序。