MySQL 从入门到精通(五):索引深度解析 —— 性能优化的核心武器
目录
一、索引概述:数据库的 “目录”
1.1 什么是索引?
1.2 索引的性能验证:用事实说话
实验环境准备
无索引查询耗时
有索引查询耗时
索引的 “空间换时间” 特性
二、索引的创建:三种核心方式
2.1 方式 1:CREATE INDEX(已存在表上创建)
2.2 方式 2:ALTER TABLE(添加索引)
2.3 方式 3:创建表时直接定义索引
三、索引的查看与分析:诊断性能的关键
3.1 查看索引的 4 种方法
方法 1:SHOW CREATE TABLE
方法 2:SHOW INDEX FROM
方法 3:EXPLAIN 分析查询计划
方法 4:查询系统表(高级)
四、索引的分类:从不同维度理解
4.1 按数据结构分类(物理存储方式)
4.2 按字段数量分类
4.3 按功能逻辑分类(最常见的面试考点)
4.4 按存储方式分类
五、索引的底层原理:为什么是 B+Tree?
5.1 为什么不选其他数据结构?
5.2 B+Tree 的核心优势
六、索引的优劣分析:权衡利弊
6.1 索引的优势
6.2 索引的弊端
七、索引设计原则:从实战中总结
7.1 必建索引的场景
7.2 避免索引的场景
7.3 其他最佳实践
八、练习与作业:动手实践
8.1 练习 1:创建带索引的表
8.2 练习 2:删除冗余索引
一、索引概述:数据库的 “目录”
1.1 什么是索引?
在数据库中,索引是一种特殊的 “数据结构文件”,它通过对表中一列或多列的值进行排序,从而加速数据的查询速度。
举个生活化的例子:查字典时,我们通过 “拼音目录” 或 “部首目录” 快速定位目标汉字,而不需要逐页翻查。数据库索引的作用类似 —— 它将表中的关键数据(如id
、name
)按特定规则排序存储,当执行WHERE
、JOIN
等查询时,数据库无需扫描全表,而是通过索引快速定位目标数据。
1.2 索引的性能验证:用事实说话
为了直观感受索引的作用,我们通过一个经典实验验证:
实验环境准备
-- 创建数据库
CREATE DATABASE mydb13_indexdb;
USE mydb13_indexdb;-- 创建无索引的student表
CREATE TABLE student(id INT, name VARCHAR(64), age INT(2)
);-- 插入百万级数据(通过自复制快速扩容)
INSERT INTO student VALUES(1,'das',20),(2,'dasdas',19),(3,'dsfsfsd',18),(4,'bbggbbg',22),(5,'eeeee',19);
INSERT INTO student SELECT * FROM student; -- 多次执行,直到数据量达到百万级
无索引查询耗时
插入一条测试数据后查询:
INSERT INTO student VALUES(666,'andy',40);
SELECT * FROM student WHERE id=666;
输出结果:
1 row in set (2.75 sec)
(查询耗时 2.75 秒,全表扫描效率极低)
有索引查询耗时
为id
字段创建索引后再次查询:
CREATE INDEX id_index ON student(id); -- 创建索引(耗时12.13秒)
SELECT * FROM student WHERE id=666;
输出结果:
1 row in set (0.00 sec)
(查询耗时几乎为 0,性能提升约 275 万倍!)
索引的 “空间换时间” 特性
创建索引前,student
表文件大小约 120MB;创建索引后,文件增大至 164MB。这是因为索引需要额外存储排序后的数据结构(如 B+Tree),但牺牲少量空间换取查询性能的大幅提升,在 “读多写少” 的业务场景中是非常划算的。
二、索引的创建:三种核心方式
2.1 方式 1:CREATE INDEX(已存在表上创建)
语法:
CREATE [UNIQUE] INDEX index_name
ON table_name (column_name(length) [ASC|DESC]);
UNIQUE
(可选):创建唯一索引(字段值不可重复)。index_name
:索引名称(建议用 “字段名 + index” 的命名规则,如id_index
)。table_name
:目标表名。column_name
:要索引的字段名。length
(可选):若字段是长文本(如VARCHAR(255)
),可指定前length
个字符作为索引(减少索引大小)。ASC/DESC
(可选):索引排序方式(默认ASC
升序)。
示例:
-- 创建普通索引(按name字段前10个字符索引)
CREATE INDEX name_index ON student(name(10));-- 创建唯一索引(id字段值必须唯一)
CREATE UNIQUE INDEX id_unique_index ON student(id);
2.2 方式 2:ALTER TABLE(添加索引)
语法:
ALTER TABLE table_name
ADD [UNIQUE] INDEX index_name (column_name(length) [ASC|DESC]);
示例:
-- 为age字段添加普通索引
ALTER TABLE student ADD INDEX age_index (age);-- 为name和age字段添加联合索引(多列索引)
ALTER TABLE student ADD INDEX name_age_index (name, age);
2.3 方式 3:创建表时直接定义索引
语法:
CREATE TABLE table_name (column1 INT,column2 VARCHAR(20),-- 普通索引INDEX index_name (column1),-- 唯一索引UNIQUE INDEX unique_index (column2),-- 主键索引(特殊的唯一索引,非空)PRIMARY KEY (column1)
);
注意:
- 主键索引(
PRIMARY KEY
)必须在创建表时指定,且字段需设置NOT NULL
。 - 多列索引需按顺序定义(如
(name, age)
),查询时只有使用第一个字段才会触发索引(“最左匹配原则”)。
示例:
-- 创建表时定义普通索引和主键索引
CREATE TABLE workinfo(id INT(10) NOT NULL AUTO_INCREMENT,name VARCHAR(20) NOT NULL,type VARCHAR(10),PRIMARY KEY (id), -- 主键索引(自动唯一且非空)INDEX name_index (name(10)) -- 普通索引(name前10字符)
);
三、索引的查看与分析:诊断性能的关键
3.1 查看索引的 4 种方法
方法 1:SHOW CREATE TABLE
语法:
SHOW CREATE TABLE table_name \G;
输出示例:
CREATE TABLE `student` (`id` int DEFAULT NULL,`name` varchar(64) DEFAULT NULL,`age` int DEFAULT NULL,KEY `id_index` (`id`) -- 显示索引信息
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
方法 2:SHOW INDEX FROM
语法:
SHOW INDEX FROM table_name \G;
输出字段解析(以student
表的id_index
为例):
字段名 | 含义 | 示例值 |
---|---|---|
Table | 索引所属的表名 | student |
Non_unique | 是否非唯一索引(0 = 唯一,1 = 非唯一) | 1 |
Key_name | 索引名称 | id_index |
Seq_in_index | 多列索引中字段的顺序(单列索引为 1) | 1 |
Column_name | 索引关联的字段名 | id |
Cardinality | 索引的基数(估计的唯一值数量,值越大索引效果越好) | 1000000 |
Sub_part | 字段中用于索引的字符长度(NULL 表示全字段) | NULL |
Index_type | 索引类型(如BTREE 、HASH ) | BTREE |
方法 3:EXPLAIN 分析查询计划
语法:
EXPLAIN SELECT * FROM table_name WHERE condition \G;
关键字段:
possible_keys
:可能使用的索引(未使用时为NULL
)。key
:实际使用的索引(核心性能指标)。rows
:扫描的行数(值越小性能越好)。
示例:
EXPLAIN SELECT * FROM student WHERE id=666 \G;
输出中key
字段显示id_index
,说明查询使用了索引;若key
为NULL
,则表示全表扫描。
方法 4:查询系统表(高级)
通过mysql.innodb_index_stats
系统表查看索引统计信息:
-- 查看指定数据库的所有索引
SELECT * FROM mysql.innodb_index_stats
WHERE database_name = 'mydb13_indexdb';-- 查看指定表的索引
SELECT * FROM mysql.innodb_index_stats
WHERE database_name = 'mydb13_indexdb' AND table_name = 'student';
四、索引的分类:从不同维度理解
4.1 按数据结构分类(物理存储方式)
MySQL 支持 4 种索引结构,最常用的是B+Tree
:
类型 | 特点 | 适用场景 |
---|---|---|
B+Tree | 所有数据存储在叶子节点,支持范围查询和排序,InnoDB 默认结构 | 常规查询、排序、分组 |
Hash | 通过哈希函数计算值的存储位置,查询速度极快但无法范围查询 | 等值查询(如WHERE id=1 ) |
R-Tree | 空间索引,用于存储地理坐标等空间数据 | GIS 系统(如地图位置查询) |
T-Tree | 适用于内存数据库,支持高并发写入 | MySQL Cluster 集群场景 |
4.2 按字段数量分类
- 单列索引:基于单个字段创建(如
INDEX (name)
)。 - 多列索引(联合索引):基于多个字段创建(如
INDEX (name, age)
),需遵循 “最左匹配原则”(只有查询条件包含前导字段时才会触发索引)。
4.3 按功能逻辑分类(最常见的面试考点)
类型 | 特点 | 示例 |
---|---|---|
普通索引 | 加速查询,无唯一性约束 | INDEX (name) |
唯一索引 | 加速查询 + 保证字段值唯一(允许NULL ) | UNIQUE INDEX (email) |
主键索引 | 特殊的唯一索引(不允许NULL ),一张表只能有一个 | PRIMARY KEY (id) |
全文索引 | 用于文本内容的模糊搜索(MyISAM 引擎支持,InnoDB 5.6 + 支持) | FULLTEXT INDEX (content) |
空间索引 | 存储和查询空间数据(如点、线、多边形) | SPATIAL INDEX (location) |
4.4 按存储方式分类
-
聚簇索引(聚集索引):
索引数据与表数据存储在一起(物理连续),一张表仅有一个聚簇索引(通常是主键)。
优点:查询效率极高(直接定位数据);
缺点:插入顺序影响性能(非顺序插入会导致页分裂)。 -
非聚簇索引(二级索引):
索引数据与表数据分开存储,通过 “键值 + 行指针” 关联。
优点:支持多字段索引;
缺点:可能需要 “回表”(先查索引,再查原表)。
五、索引的底层原理:为什么是 B+Tree?
5.1 为什么不选其他数据结构?
- Hash 索引:虽然等值查询快,但无法排序和范围查询(如
WHERE age>20
)。 - 二叉树:可能退化为链表(如所有节点只有右子树),导致查询时间复杂度从
O(logN)
变为O(N)
。 - 平衡二叉树(AVL):插入 / 删除时需要频繁旋转调整,维护成本高,且单节点存储数据少(磁盘 IO 次数多)。
5.2 B+Tree 的核心优势
B+Tree 是 B-Tree 的变种,其核心设计如下:
- 所有数据存储在叶子节点(非叶子节点仅存索引),叶子节点通过指针连接(支持范围查询)。
- 非叶子节点可以存储更多索引键(通过增加子节点数降低树的高度),减少磁盘 IO 次数。
InnoDB 的 B+Tree 实现:
- 叶子节点存储完整数据(聚簇索引)或主键值(非聚簇索引)。
- 单个节点大小为 16KB(InnoDB 默认页大小),可存储大量索引键(如
INT
类型可存约 4000 个键)。
六、索引的优劣分析:权衡利弊
6.1 索引的优势
- 查询速度提升:百万级数据查询从秒级到毫秒级(如前文实验)。
- 唯一性约束:唯一索引可替代
UNIQUE
约束,避免重复数据。 - 排序 / 分组加速:索引已排序,
ORDER BY
和GROUP BY
无需额外扫描。 - 连表查询优化:主外键字段的索引可大幅减少 JOIN 时的扫描行数。
6.2 索引的弊端
- 空间占用:索引文件可能占原表空间的 20%-50%(如 120MB 的表可能需要 164MB 的索引)。
- 写入性能下降:插入 / 更新 / 删除时需同步维护索引(B+Tree 的分裂、合并操作)。
- 过度索引风险:索引越多,维护成本越高,可能导致写入性能骤降。
七、索引设计原则:从实战中总结
7.1 必建索引的场景
- 高频查询字段:如
WHERE
、JOIN
、ORDER BY
中的字段。 - 唯一性高的字段:如用户
id
(基数大,索引效果好)。 - 排序 / 分组字段:索引已排序,避免全表扫描后再排序。
7.2 避免索引的场景
- 低基数字段:如 “性别”(只有
男/女
),索引无法有效过滤数据。 - 频繁更新的字段:如订单状态(
待支付/已支付
),索引维护成本高。 - 长文本字段:如
TEXT
类型,建议使用前缀索引(如前 10 个字符)。
7.3 其他最佳实践
- 限制索引数量:单表索引数建议不超过 5 个(过多索引影响写入)。
- 多列索引的顺序:将高频查询字段、高基数字段放在前面(如
(name, age)
优于(age, name)
)。 - 定期清理冗余索引:通过
SHOW INDEX
检查未使用的索引(Cardinality
低的索引可删除)。
八、练习与作业:动手实践
8.1 练习 1:创建带索引的表
需求:新建数据库mydb14_job
,创建workinfo
表,要求:
id
字段为主键,自增,唯一索引(降序)。name
字段创建长度为 10 的普通索引。type
和address
字段创建联合索引。
实现步骤:
-- 创建数据库
CREATE DATABASE mydb14_job;
USE mydb14_job;-- 创建表并定义索引
CREATE TABLE workinfo(id INT(10) NOT NULL AUTO_INCREMENT,name VARCHAR(20) NOT NULL,type VARCHAR(10),address VARCHAR(50),wage INT,content TINYTEXT,extra TEXT,PRIMARY KEY (id), -- 主键索引(聚簇索引)UNIQUE INDEX index_id (id DESC), -- 唯一索引(降序)INDEX index_name (name(10)), -- 普通索引(前缀10字符)INDEX index_t (type, address) -- 联合索引
);-- 验证索引
SHOW INDEX FROM workinfo \G;
8.2 练习 2:删除冗余索引
需求:删除workinfo
表的index_id
唯一索引。
DROP INDEX index_id ON workinfo;
下一篇预告:《MySQL 从入门到精通(七):视图全面详解 —— 虚拟表的灵活运用》,将深入讲解视图的创建、更新、修改与删除,以及如何通过视图简化复杂查询、提升数据安全性。
相关文章:
MySQL 从入门到精通(五):索引深度解析 —— 性能优化的核心武器
目录 一、索引概述:数据库的 “目录” 1.1 什么是索引? 1.2 索引的性能验证:用事实说话 实验环境准备 无索引查询耗时 有索引查询耗时 索引的 “空间换时间” 特性 二、索引的创建:三种核心方式 2.1 方式 1:C…...
spark-Join Key 的基数/rand函数
在数据处理中,Join Key 的基数 是指 Join Key 的唯一值的数量(也称为 Distinct Key Count)。它表示某个字段(即 Join Key)在数据集中有多少个不同的值。 1. Join Key 基数的意义 高基数:Join Key 的唯一值…...

LLMs之ChatGPT:《Connecting GitHub to ChatGPT deep research》翻译与解读
LLMs之ChatGPT:《Connecting GitHub to ChatGPT deep research》翻译与解读 导读:这篇OpenAI帮助文档全面介绍了将GitHub连接到ChatGPT进行深度代码研究的方法、优势和注意事项。通过连接GitHub,用户可以充分利用ChatGPT强大的代码理解和生成…...

【桌面】【输入法】常见问题汇总
目录 一、麒麟桌面系统输入法概述 1、输入法介绍 2、输入法相关组件与服务 3、输入法调试相关命令 3.1、输入法诊断命令 3.2、输入法配置重新加载命令 3.3、启动fcitx输入法 3.4、查看输入法有哪些版本,并安装指定版本 3.5、重启输入法 3.6、查看fcitx进程…...
R语言学习--Day01--数据清洗初了解andR的经典筛选语法
当我们在拿到一份数据时,是否遇到过想要分析数据却无从下手?通过编程语言去利用它时发现有很多报错不是来源于代码而是因为数据里有很多脏数据;在这个时候,如果你会用R语言来对数据进行清洗,这会让你的效率提升很多。 …...

QT的初始代码解读及其布局和弹簧
this指的是真正的当前正在显示的窗口 main函数: Widget w是生成了一个主窗口,QT Designer是在这个主窗口里塞组件 w.show()用来展示这个主窗口 头文件: namespace Ui{class Widget;}中的class Widget和下面的class Widget不是一个东西 Ui…...

Profinet转CanOpen网关,打破协议壁垒的关键技术
在石油化工行业的生产现场,各类自动化设备如同精密运转的神经系统,而通信协议则是传递信号的"语言"。当不同厂商的设备采用Canopen与Profinet这两种主流工业协议时,就像两个使用不同方言的专家需要实时协作,此时开疆智能…...

引用第三方自定义组件——微信小程序学习笔记
1. 使用 npm 安装第三方包 1.1 下载安装Node.js 工具 下载地址:Node.js — Download Node.js 1.2 安装 npm 包 在项目空白处右键弹出菜单,选择“在外部终端窗口打开”,打开命令行工具,输入以下指令: 1> 初始化:…...
Docker、Docker-compose、K8s、Docker swarm之间的区别
1.Docker docker是一个运行于主流linux/windows系统上的应用容器引擎,通过docker中的镜像(image)可以在docker中构建一个独立的容器(container)来运行镜像对应的服务; 例如可以通过mysql镜像构建一个运行mysql的容器,既可以直接进入该容器命…...

SpringAI实现AI应用-使用redis持久化聊天记忆
SpringAI实战链接 1.SpringAl实现AI应用-快速搭建-CSDN博客 2.SpringAI实现AI应用-搭建知识库-CSDN博客 3.SpringAI实现AI应用-内置顾问-CSDN博客 4.SpringAI实现AI应用-使用redis持久化聊天记忆-CSDN博客 5.SpringAI实现AI应用-自定义顾问(Advisor)…...

C#问题 加载格式不正确解决方法
出现上面问题 解决办法:C#问题 改成x86 不要选择anycpu...
VTK|结合qt创建通用按钮控制显隐(边框、坐标轴、点线面)
文章目录 增加边框BoundingBox添加addBoundingBox添加BoundingBox控制按钮点击按钮之后的槽函数 添加坐标轴增加点线面显隐控制按钮添加控制点线面显隐的按钮到三维显示界面控制面显示槽函数控制线显示槽函数控制点显示槽函数 增加边框BoundingBox 增加边框BoundingBox并通过按…...

CentOS 7.9 安装详解:手动分区完全指南
CentOS 7.9 安装详解:手动分区完全指南 为什么需要手动分区?CentOS 7.9 基本分区说明1. /boot/efi 分区2. /boot 分区3. swap 交换分区4. / (根) 分区 可选分区(进阶设置)5. /home 分区6. /var 分区7. /tmp 分区 分区方案建议标准…...
在过滤器中获取body中的json数据并且使得后续的controller层也能获取使用
前景提示: ①我需要在filter中获取到json数据->对key名首字母进行排序,然后拼接,进行验签 ②所以就需要在filer获取到json的数据,因为请求数据是一次性读取的流。如果过滤器中调用了request.json或request.get_json()ÿ…...

如何使用测试软件 Jmeter
第一步,点击 编辑 添加线程组 第二步,右键单击线程组,添加取样器 HTTP 请求 第三步,设置请求路径 第四步,添加 查看结果树 用于查看请求响应 最后点击绿色小三角启动即可...

2025盘古石初赛WP
来不及做,还有n道题待填坑 文章目录 手机取证 Mobile Forensics分析安卓手机检材,手机的IMSI是? [答案格式:660336842291717]养鱼诈骗投资1000,五天后收益是? [答案格式:123]分析苹果手机检材&a…...

系统分析与设计期末复习
第一章 系统的五个特性 整体性、目的性、相关性、环境适应性、层次性 软件系统的四个特性 复杂性、一致性、可变性、不可见性 第二章 系统规划 系统开发生命周期 系统规划->系统分析->系统设计->系统实施->系统运行维护->系统规划 诺兰阶段模型 阶段&a…...
最大公约数gcd和最小公倍数lcm
一、相关公式及其性质 文章只服务于竞赛,所以不会涉及证明。 辗转相除法:gcd(a, b) gcd(b, a % b); 直到 b 0,就可以知道上一层递归中的 a % b 0,所以上一层的 b 就是答案,也就是这一层递归的 a gcd(a, b) * lcm…...

IBM BAW(原BPM升级版)使用教程第八讲
续前篇! 一、流程开发功能模块使用逻辑和顺序 前面我们已经对 流程、用户界面、公开的自动化服务、服务、事件、团队、数据、性能、文件各个模块进行了详细讲解,现在统一进行全面统一讲解。 在 IBM Business Automation Workflow (BAW) 中,…...
视觉革命来袭!ComfyUI-LTXVideo 让视频创作更高效
探索LTX-Video 支持的ComfyUI 在数字化视频创作领域,视频制作效果的提升对创作者来说无疑是一项重要的突破。LTX-Video支持的ComfyUI便是这样一款提供自定义节点的工具集,它专为改善视频质量、提升生成速度而开发。接下来,我们将详细介绍其功…...

从电动化到智能化,法雷奥“猛攻”中国汽车市场
当前,全球汽车产业正在经历前所未有的变革,外资Tier1巨头开始向中国智能电动汽车市场发起新一轮“猛攻”。 在4月23日-5月2日上海国际车展期间,博世、采埃孚、大陆集团、法雷奥等全球百强零部件厂商纷纷发布战略新品与转型计划。在这其中&am…...

鸿蒙开发——3.ArkTS声明式开发:构建第一个ArkTS应用
鸿蒙开发——3.ArkTS声明式开发:构建第一个ArkTS应用 一、创建ArkTS工程二、ArkTS工程目录结构(Stage模型)三、构建第一个页面四、构建第二个页面五、实现页面之间的跳转六、模拟器运行 一、创建ArkTS工程 1、若首次打开DevEco Studio,请点击…...

word换行符和段落标记
换行符:只换行不分段 作用:我们需要对它进行分段,但它是一个信息群组,我希望它们有同样的段落格式! 快捷键:shiftenter 段落标记:分段 快捷键:enter 修改字体格式或段落格式 …...

AI时代的数据可视化:未来已来
你有没有想过,数据可视化在未来会变成什么样?随着人工智能(AI)的飞速发展,数据可视化已经不再是简单的图表和图形,而是一个充满无限可能的智能领域。AI时代的可视化不仅能自动解读数据,还能预测…...
spark基本介绍
Spark 是基于内存计算的分布式大数据处理框架,由加州大学伯克利分校 AMPLab 开发,现已成为 Apache 顶级项目。以下是其核心要点: 核心特点 1. 内存计算:数据可驻留内存,大幅提升迭代计算(如机器学习、图计算…...

深入理解 TCP:重传机制、滑动窗口、流量控制与拥塞控制
TCP(Transmission Control Protocol)是一个面向连接、可靠传输的协议,支撑着绝大多数互联网通信。在实现可靠性的背后,TCP 引入了多个关键机制:重传机制、滑动窗口、流量控制 和 拥塞控制。这些机制共同协作࿰…...
MySQL 窗口函数入门到精通
目录 常用窗口函数速查表 1. 什么是"窗口"(不是你想的那种窗口) "窗口"≠电脑界面的窗口 那么,SQL 中的"窗口"是什么? 用表格形式理解"窗口"概念 2. 窗口函数解决了什么问题 场景…...

uniapp-商城-51-后台 商家信息(logo处理)
前面对页面基本进行了梳理和说明,特别是对验证规则进行了阐述,并对自定义规则的兼容性进行了特别补充,应该说是干货满满。不知道有没有小伙伴已经消化了。 下面我们继续前进,说说页面上的logo上传组件,主要就是uni-fil…...
✍️【TS类型体操进阶】挑战类型极限,成为类型魔法师!♂️✨
哈喽类型战士们!今天我们要玩转TS类型体操,让你的类型系统像体操运动员一样灵活优雅~ 学会这些绝招,保准你的代码类型稳如老狗!(文末附类型体操段位表)🚀 一、什么是类型体操? &…...
联邦学习图像分类实战:基于FATE与PyTorch的隐私保护机器学习系统构建指南
引言 在数据孤岛与隐私保护需求并存的今天,联邦学习(Federated Learning)作为分布式机器学习范式,为医疗影像分析、金融风控、智能交通等领域提供了创新解决方案。本文将基于FATE框架与PyTorch深度学习框架,详细阐述如…...