MySQL索引------从入门到出土
文章目录
- 索引
- 概念
- 优缺点
- 索引的种类
- [1. 普通索引和唯一索引](#1. 普通索引和唯一索引 "#1-%E6%99%AE%E9%80%9A%E7%B4%A2%E5%BC%95%E5%92%8C%E5%94%AF%E4%B8%80%E7%B4%A2%E5%BC%95")
- [2. 单列索引和组合索引](#2. 单列索引和组合索引 "#2-%E5%8D%95%E5%88%97%E7%B4%A2%E5%BC%95%E5%92%8C%E7%BB%84%E5%90%88%E7%B4%A2%E5%BC%95")
- [3. 全文索引](#3. 全文索引 "#3-%E5%85%A8%E6%96%87%E7%B4%A2%E5%BC%95")
- [4. 空间索引](#4. 空间索引 "#4-%E7%A9%BA%E9%97%B4%E7%B4%A2%E5%BC%95")
- MySQL索引操作
- 索引数据结构深度解析
- MySQL为什么选择B+Tree作为索引结构
- [1. B+Tree vs B Tree](#1. B+Tree vs B Tree "#1-btree-vs-b-tree")
- [2. B+Tree vs 二叉树](#2. B+Tree vs 二叉树 "#2-btree-vs-%E4%BA%8C%E5%8F%89%E6%A0%91")
- [3. B+Tree vs Hash](#3. B+Tree vs Hash "#3-btree-vs-hash")
- MySQL为什么选择B+Tree作为索引结构
- 索引使用最佳实践
索引
概念
索引是一个单独的、存储在磁盘上的数据库结构,包含着对数据表里所有记录的引用指针。
索引的核心价值在于能够快速定位到某个或多个列中具有特定值的行。所有MySQL列类型都可以被索引,合理使用索引是提升查询性能的最有效途径。
需要注意的是,索引是在存储引擎层实现的,因此不同存储引擎的索引实现方式存在差异,支持的类型也不尽相同。MySQL中索引的存储类型主要有BTREE和HASH两种,具体取决于表的存储引擎:
- MyISAM和InnoDB存储引擎只支持BTREE索引
- MEMORY/HEAP存储引擎可以支持HASH和BTREE索引
优缺点
优点
索引的主要优势体现在以下几个方面:
- 保证数据唯一性:通过创建唯一索引,确保表中每行数据的唯一性
- 大幅提升查询速度:这是创建索引最主要的目的
- 加速表间连接:在实现数据参考完整性时,显著提升多表连接查询效率
- 优化分组排序:在使用GROUP BY和ORDER BY子句时,显著减少查询中分组和排序的时间消耗
缺点
索引虽然强大,但也存在一些不容忽视的缺点:
- 创建和维护成本:创建索引和维护索引需要消耗时间,且随着数据量增长,维护成本相应增加
- 占用磁盘空间:每个索引都需要占用额外的物理空间,大量索引可能导致索引文件比数据文件更大
- 影响DML操作性能:当对表进行增删改操作时,索引需要动态维护,这会降低数据维护速度
索引的种类
MySQL支持多种索引类型,满足不同的业务需求:
1. 普通索引和唯一索引
普通索引是MySQL中最基本的索引类型,允许在索引列中插入重复值和空值。
唯一索引要求索引列的值必须唯一,但允许有空值。对于组合索引,则要求列值的组合必须唯一。
注意:主键索引是一种特殊的唯一索引,不允许有空值。
2. 单列索引和组合索引
单列索引只包含单个列,一个表可以创建多个单列索引。
组合索引在表的多个字段组合上创建,查询时只有使用这些字段的最左前缀时,索引才会被有效利用。
注意:使用组合索引时必须遵循最左前缀原则。
3. 全文索引
全文索引类型为FULLTEXT,支持对文本内容进行全文检索,允许在索引列中插入重复值和空值。
全文索引适用于CHAR、VARCHAR或TEXT类型的列,特别适合文本搜索场景。
4. 空间索引
空间索引是针对空间数据类型字段建立的索引,MySQL支持GEOMETRY、POINT、LINESTRING和POLYGON四种空间数据类型。
使用SPATIAL关键字可以创建空间索引,创建空间索引的列必须声明为NOT NULL,且目前只能在MyISAM存储引擎的表中创建。
MySQL索引操作
如何创建及保存MySQL的索引
MySQL提供了多种灵活的索引创建方式:
在创建表的时候创建索引
使用CREATE TABLE语句时,除了定义列数据类型外,还可以通过定义约束来隐式创建索引:
sql
CREATE TABLE table_name [col_name data_type]
[UNIQUE|FULLTEXT|SPATIAL] [INDEX|KEY] [index_name] (col_name [length])
[ASC|DESC]
参数说明:
- UNIQUE、FULLTEXT、SPATIAL:分别表示唯一索引、全文索引和空间索引
- INDEX与KEY:同义词,用于指定创建索引
示例:在id字段上创建唯一索引
sql
CREATE TABLE t1 (
id INT NOT NULL,
name CHAR(30) NOT NULL,
UNIQUE INDEX UniqIdx(id)
);

在已存在的表上创建索引
对于已存在的表,可以使用ALTER TABLE或CREATE INDEX语句添加索引。
使用ALTER TABLE创建索引:
sql
ALTER TABLE table_name ADD
[UNIQUE|FULLTEXT|SPATIAL] [INDEX|KEY] [index_name] (col_name[length],...)
[ASC|DESC]
示例:
sql
-- 创建book表
CREATE TABLE book (
id INT NOT NULL,
name CHAR(30) NOT NULL
);
-- 使用ALTER TABLE创建唯一索引
ALTER TABLE book ADD UNIQUE INDEX UniqidIdx (id);

使用CREATE INDEX创建索引:
sql
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
ON table_name (col_name [length],...)
[ASC|DESC]
示例:
sql
-- 重建book表
DROP TABLE book;
CREATE TABLE book (
id INT NOT NULL,
name CHAR(30) NOT NULL
);
-- 使用CREATE INDEX创建唯一索引
CREATE UNIQUE INDEX UniqidIdx ON book (id);

索引数据结构深度解析
MySQL为什么选择B+Tree作为索引结构
B+Tree之所以成为MySQL索引的首选数据结构,是因为它在多种场景下都表现出优越的性能:
1. B+Tree vs B Tree
- 存储效率:B+Tree只在叶子节点存储数据,而非叶子节点仅存储键值,使得单个节点数据量更小,在相同磁盘I/O次数下能查询更多节点
- 范围查询:B+Tree叶子节点通过双链表连接,完美支持基于范围的顺序查找,而B树无法高效实现这一功能
2. B+Tree vs 二叉树
- 查询复杂度:对于有N个叶子节点的B+Tree,搜索复杂度为O(logdN),其中d表示节点允许的最大子节点数(通常超过100)
- 树高度控制 :即使数据量达到千万级别,B+Tree的高度仍能维持在3
4层,仅需34次磁盘I/O操作即可定位目标数据 - 性能对比:二叉树的搜索复杂度为O(logN),且每个父节点只能有2个子节点,检索目标数据需要更多的磁盘I/O次数
3. B+Tree vs Hash
- 等值查询:Hash表在等值查询时效率极高,复杂度为O(1)
- 范围查询:Hash表不适合范围查询,而B+Tree在等值查询和范围查询场景下都有良好表现,适用性更广
索引使用最佳实践
MySQL怎么判断要不要加索引
创建索引应遵循以下原则:
- 唯一性约束:当数据本身具有唯一性特征时,应创建唯一索引,既保证数据完整性又提升查询速度
- 排序分组优化:对频繁进行GROUP BY或ORDER BY操作的列建立索引,多列操作时可创建组合索引
只要创建了索引就一定会走索引吗
答案是否定的。
特别是在使用组合索引时,如果不遵循"最左前缀"原则,索引将无法生效。
示例:假设在(id, name, age)字段上建立了组合索引MultiIdx,索引按id、name、age顺序存储。以下查询能使用索引:
- WHERE id = ?
- WHERE id = ? AND name = ?
- WHERE id = ? AND name = ? AND age = ?
而以下查询无法使用该索引:
- WHERE age = ?
- WHERE name = ? AND age = ?
如何判断数据库的索引有没有生效
使用EXPLAIN语句可以分析索引使用情况:
sql
-- 分析查询语句执行计划
EXPLAIN SELECT * FROM user WHERE id=3;
完整示例:
sql
# 创建用户表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名称',
`birthday` datetime(0) NULL DEFAULT NULL COMMENT '生日',
`sex` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '性别',
`address` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- 插入测试数据
INSERT INTO `user` VALUES (1, '玉如', '2019-02-27 17:47:08', '男', '北京市西区');
INSERT INTO `user` VALUES (2, '晓兰', '2019-03-02 15:09:37', '男', '北京市东区');
INSERT INTO `user` VALUES (3, '花蝶', '2019-03-04 11:34:34', '女', '北京市青湖区');
INSERT INTO `user` VALUES (4, '兰咯', '2019-03-04 12:04:06', '女', '北京市青谱区');
INSERT INTO `user` VALUES (5, '咕咕', '2019-03-07 17:37:26', '女', '北京市红滩区');
INSERT INTO `user` VALUES (6, '嘻嘻', '2019-03-08 11:44:00', '男', '北京市新区');
INSERT INTO `user` VALUES (7, '萌萌', '2019-04-08 11:44:00', '男', '北京市西区');
-- 创建唯一索引
CREATE UNIQUE INDEX UniqidIdx ON user (id);
-- 分析查询执行计划
EXPLAIN SELECT * FROM user WHERE id=3;

EXPLAIN结果中关键字段:
possible_keys:MySQL可能选用的索引key:MySQL实际使用的索引
如果这两个字段都显示使用了id索引,说明索引生效。
如何评估一个索引创建的是否合理
合理的索引设计应遵循以下原则:
- 更新频繁的表要谨慎:避免对经常更新的表创建过多索引,索引列应尽可能少
- 小表不建索引:数据量小的表查询花费时间可能比遍历索引时间更短,索引反而可能降低性能
- 选择高区分度列:在不同值较多的列上建立索引,避免在低区分度列(如性别)上建索引
- 确保数据唯一性:当数据本身具有唯一性特征时,应创建唯一索引
- 优化排序分组:对频繁进行排序或分组的列建立索引,多列操作时使用组合索引
索引是越多越好吗
绝对不是。
过多的索引会带来以下问题:
- 占用大量磁盘空间
- 影响INSERT、DELETE、UPDATE等DML操作性能
- 增加索引维护成本,数据变更时需要同步更新所有相关索引
数据库索引失效了怎么办
避免索引失效的实用技巧:
- 遵循最左前缀原则:使用组合索引时必须从最左列开始
- 避免索引列操作:不在索引列上进行计算、函数调用或类型转换
- 优先使用覆盖索引:尽量只查询索引列,避免SELECT *
- 慎用不等于操作:!=或<>操作可能导致全表扫描
- 注意LIKE查询:LIKE以通配符开头('%abc')会导致索引失效
- 确保类型匹配:字符串不加单引号可能导致隐式类型转换,使索引失效
- 减少OR使用:OR连接条件时可能会使索引失效
所有的字段都适合创建索引吗
当然不是。
以下情况不适合创建索引:
- 频繁更新的字段:索引维护成本过高
- WHERE条件中不使用的字段:创建了也用不上
- 数据量小的表:全表扫描可能更快
- 低区分度字段:如性别、真假值等重复且分布均匀的字段
- 参与列计算的列:无法有效利用索引