一、基础概念
索引是数据库中对一列或多列值进行排序的数据结构。其主要作用是加快数据检索速度,相当于书本的目录。本质是存储引擎层面的实现,不同存储引擎索引实现不同。
1. 索引的本质
- 数据结构:本质上是特殊的查找数据结构,最常见的是B+树结构。
- 存储位置:独立于表数据之外的额外存储结构。
- 实现层面:由存储引擎实现而非服务器层,不同引擎实现方式不同。
2. 核心作用原理
- 加速查询:通过减少全表扫描,将O(n)复杂度降至O(log n)。
- 排序优化:对ORDER BY操作避免filesort临时表排序。
- 连接效率:提高表连接操作的匹配速度。
3. 关键特性
- 选择性:索引列不同值的数量与总行数的比值(Cardinality)。
- 覆盖索引:查询列都包含在索引中时,无需回表查数据。
- 最左前缀:复合索引使用时必须从最左列开始匹配。
4. 存储结构对比
存储引擎 | 默认索引类型 | 特性 |
---|---|---|
InnoDB | B+Tree | 支持事务、行锁、聚集索引 |
MyISAM | B+Tree | 表锁、非聚集索引 |
Memory | Hash | 内存表、极速查询 |
二、索引类型
- B-Tree索引:最常见的索引类型,适用于全键值、键值范围或键前缀查找。
- 哈希索引:基于哈希表实现,只支持等值比较查询。
- 全文索引:用于文本内容的搜索。
- 空间索引:用于地理空间数据类型。
1. B-Tree索引(B+树索引)
实现方式:
css
基于B+树数据结构实现。
InnoDB存储引擎的默认索引类型。
特点:
所有值按顺序存储。
每个叶子节点到根节点的距离相同。
适合全键值、键值范围或键前缀查找。
适用场景:
scss
精确匹配(=, IN)。
范围查询(>, <, BETWEEN)。
前缀匹配(LIKE 'abc%')。
排序(ORDER BY)。
限制:
css
必须按最左前缀查找。
不能跳过索引中的列。
如果查询中有范围查询,B+树的有序性被范围查询打断,后面的列无法继续使用索引。
2. 哈希索引
实现方式:
基于哈希表实现。
Memory引擎默认索引类型。
InnoDB中并非传统意义的哈希索引,主键或唯一键的等值查询。
特点:
less
只支持等值比较查询(=, <=>)。
查询速度非常快(O(1)时间复杂度)。
不存储字段值,只存储哈希值和行指针。
InnoDB自动为"热点"B+Tree索引页构建内存哈希索引,完全自动管理,无需用户干预。
优点:
适合精确查询,性能极高。
缺点:
不支持排序。
不支持范围查询。
不支持部分索引列查询。
存在哈希冲突可能。
与传统哈希索引的区别:
特性 | InnoDB自适应哈希索引 | 传统哈希索引 |
---|---|---|
存储位置 | 内存中 | 磁盘/内存 |
管理方式 | 自动 | 手动创建 |
持久性 | 非持久(重启消失) | 可持久化 |
范围查询 | 不支持 | 不支持 |
等值查询 | 极快 | 极快 |
3. 全文索引(Full-text)
实现方式:
perl
InnoDB和MyISAM都支持。InnoDB从MySQL 5.6开始支持,具备事务特性。
使用倒排索引(inverted index)存储单词及其位置信息。
特点:
scss
专门用于文本内容的搜索。
可以对CHAR、VARCHAR和TEXT列创建。
支持自然语言搜索和布尔搜索。
工作原理:
scss
分词机制:按空格/标点分割单词;移除停用词(the, and等常见词);存储词干(部分语言支持)。
相关性计算:基于TF-IDF(词频-逆文档频率)算法;考虑词在文档中的出现频率;考虑词在全集中的稀有程度。
使用方式(sql):
sql
-- 创建全文索引
ALTER TABLE articles ADD FULLTEXT(title, body);
-- 使用全文索引搜索
SELECT * FROM articles WHERE MATCH(title, body) AGAINST('MySQL索引' IN NATURAL LANGUAGE MODE);
4. 空间索引(Spatial)
实现方式:
scss
R-Tree数据结构。
MyISAM和InnoDB(MySQL 5.7+)支持。
特点:
scss
用于地理空间数据类型(GEOMETRY, POINT, LINESTRING, POLYGON等)。
支持空间关系函数计算(ST_Contains, ST_Distance等)。
使用示例(sql):
sql
-- 创建空间索引
CREATE TABLE geom (g GEOMETRY NOT NULL, SPATIAL INDEX(g));
-- 空间查询
SELECT * FROM geom WHERE ST_Contains(g, Point(1,1));
5. 复合索引(多列索引)
特点:
css
在多个列上建立的索引,构建B+树结构。
遵循最左前缀原则。查找时相当于使用"a_b_c"这样的组合键进行匹配
列顺序非常重要。如INDEX(a,b,c)会先按a排序,a相同按b排序,b相同再按c排序。
使用建议:
将选择性高的列放在前面。
考虑查询的常用条件顺序。
避免创建过多复合索引。
6. 覆盖索引
特点:
查询只需要通过索引就能获取所需数据。
不需要回表查询数据行。
可以显著提高查询性能。
使用示例(sql):
sql
-- 如果索引包含(col1, col2)
SELECT col1, col2 FROM table WHERE col1 = 'value'; -- 覆盖索引
SELECT * FROM table WHERE col1 = 'value'; -- 不是覆盖索引
每种索引类型都有其适用的场景,在实际应用中应该根据查询模式和数据特点选择合适的索引类型。对于大多数OLTP应用,B-Tree索引是最常用的选择;全文索引适合文本搜索场景;空间索引则专用于地理空间数据处理。
三、索引优缺点
1. 索引的核心优势
1.1 查询加速机制
- 数据结构优化:B+树索引将查询复杂度从O(n)降至O(log n),通过树形结构快速定位数据。
- 减少IO操作:索引体积通常远小于表数据,加载更少数据页到内存(如3层B+树可支撑千万级数据)。
- 预排序特性:索引本身有序,对ORDER BY/GROUP BY操作避免临时表排序(Extra显示"Using index")
- 示例场景:10万行数据的用户表,无索引的name查询需扫描全表,建立索引后仅需3-4次IO即可定位。 1.2 锁与并发优化
- 行锁升级:InnoDB通过索引实现行锁,无索引会退化为表锁(如UPDATE未走索引将锁全表)
- MVCC支持:索引帮助多版本控制快速定位可见版本,提升并发读性能 1.3 覆盖索引特性 当查询列都包含在索引中时,引擎可直接从索引获取数据,避免回表操作。如: -- 有索引(a,b) SELECT a,b FROM table WHERE a=1 -- 完全通过索引完成
2. 索引的性能代价
2.1 写入开销放大
- B+树维护成本:每次INSERT需更新索引树(页分裂概率约5-10%),导致写入速度下降30-50%。
- 日志膨胀:InnoDB需记录索引变更到redo log,增加IO压力。
- 性能影响:表添加5个索引后,写入TPS可能从2000降至1200。 2.2 存储空间占用
- 典型占用比例:索引空间通常达数据量的20-30%,复合索引列越多空间增长越快
- 内存压力:BP缓冲池需缓存索引页,可能挤占数据页空间 2.3 优化器误判风险
- 统计信息滞后:当数据分布变化但统计信息未更新时(如20%数据突变),可能选择错误索引。
- 索引合并代价:优化器选择合并多个单列索引时,性能可能不如预期。
3. 底层数据结构影响
3.1 B+树索引特性
- 平衡写入与查询: 相比B树,B+树通过非叶子节点仅存键值的设计,实现:更高扇出(通常500+子节点);更稳定的查询性能(任何查询路径长度相同);
- 范围查询优化: 叶子节点链表结构特别适合范围扫描 3.2 哈希索引局限
- 内存依赖:自适应哈希索引仅内存有效,重启后需重新构建。
- 精确匹配限定:仅支持=、IN操作,无法用于排序或范围查询。
四、使用实践
- 选择区分度高的列建索引。
- 避免对频繁更新的列建索引。
- 考虑索引列的数据类型大小。
- 多列索引遵循最左前缀原则。
1. 使用事项
1.1 创建原则:
sql
常用于WHERE、JOIN、ORDER BY的列。
高选择性的列优先,选择性阈值:Cardinality/总行数 > 10% 的列适合建索引。
避免过度索引(一般表不超过5-6个)。
1.2 使用限制:
ini
索引列不能参与计算(如WHERE col+1=5)。
字符串前缀索引长度需要合理设置。
注意NULL值对索引使用的影响。
2. 性能影响
2.1 正向影响:
css
读操作性能提升10-100倍。
降低服务器CPU消耗。
减少磁盘I/O压力。
2.2 负面影响:
erlang
写操作需要维护索引结构数据。
占用额外存储空间(约增加20-30%)。
索引碎片化需要定期维护。
理解这些基本概念是进行有效索引优化的重要基础,实际应用中需要结合具体查询模式和数据特征进行索引设计和调整。
3. 最左前缀原则
3.1 基本规则:
查询必须从索引最左边的列开始使用
不能跳过中间的列直接使用后面的列
3.2 有效使用示例:
css
-- 索引:INDEX(a,b,c)
-- 有效使用(使用a):
WHERE a = 1
-- 有效使用(使用a,b)
WHERE a = 1 AND b = 2
-- 有效使用(使用a,b,c)
WHERE a = 1 AND b = 2 AND c = 3
3.3 无效使用示例:
css
-- 无效(未使用a)
WHERE b = 2
-- 无效(跳过了b)
WHERE a = 1 AND c = 3
3.4 范围查询的影响
关键规则:
sql
范围查询(>, <, BETWEEN, LIKE等)会使后面的索引列失效
范围查询列自身仍能使用索引
示例分析:
css
-- 索引:INDEX(a,b,c)
-- 仅a,b能用索引(c失效)
WHERE a = 1 AND b > 2 AND c = 3
-- 仅a能用索引(b,c失效)
WHERE a > 1 AND b = 2 AND c = 3
理解复合索引的使用逻辑需要结合B+树的结构特点,合理设计复合索引可以显著提升查询性能,但也需要避免过度索引导致的写性能下降和维护成本增加。
4. 其它
4.1 监控指标
- 索引使用情况监控,查找无用索引
sql
SELECT * FROM sys.schema_unused_indexes;
- 索引效率分析,查看Cardinality(不重复值的预估数量,比值越趋近0时索引效果越差)
sql
SHOW INDEX FROM table_name;
- Cardinality:
sql
不重复值的预估数量。选择性公式:Cardinality/表总行数,比值趋近0时索引效果差
4.2 特殊场景优化
-
前缀索引:对长字符串使用INDEX(column(10))指定前缀长度
-
函数索引:MySQL 8.0+支持INDEX((UPPER(column)))表达式索引 4.3 排序与分组优化
-
ORDER BY优化:
当排序顺序与索引顺序一致时,可避免filesort,同样遵循最左前缀原则。
-
GROUP BY优化:
分组操作也可以利用复合索引,需要满足最左前缀且中间无间隔。
作者:飞蓬