MySQL 从入门到精通(五):索引深度解析 —— 性能优化的核心武器

news2025/5/11 16:11:41

目录

一、索引概述:数据库的 “目录”

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 什么是索引?

在数据库中,索引是一种特殊的 “数据结构文件”,它通过对表中一列或多列的值进行排序,从而加速数据的查询速度。
举个生活化的例子:查字典时,我们通过 “拼音目录” 或 “部首目录” 快速定位目标汉字,而不需要逐页翻查。数据库索引的作用类似 —— 它将表中的关键数据(如idname)按特定规则排序存储,当执行WHEREJOIN等查询时,数据库无需扫描全表,而是通过索引快速定位目标数据。

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索引类型(如BTREEHASHBTREE
方法 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,说明查询使用了索引;若keyNULL,则表示全表扫描。

方法 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)
唯一索引加速查询 + 保证字段值唯一(允许NULLUNIQUE 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 BYGROUP BY无需额外扫描。
  • 连表查询优化:主外键字段的索引可大幅减少 JOIN 时的扫描行数。

6.2 索引的弊端

  • 空间占用:索引文件可能占原表空间的 20%-50%(如 120MB 的表可能需要 164MB 的索引)。
  • 写入性能下降:插入 / 更新 / 删除时需同步维护索引(B+Tree 的分裂、合并操作)。
  • 过度索引风险:索引越多,维护成本越高,可能导致写入性能骤降。

七、索引设计原则:从实战中总结

7.1 必建索引的场景

  • 高频查询字段:如WHEREJOINORDER 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 的普通索引。
  • typeaddress字段创建联合索引。

实现步骤

-- 创建数据库
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 从入门到精通(七):视图全面详解 —— 虚拟表的灵活运用》,将深入讲解视图的创建、更新、修改与删除,以及如何通过视图简化复杂查询、提升数据安全性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2373247.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

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进程…

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> 初始化:…

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&#xff09…

C#问题 加载格式不正确解决方法

出现上面问题 解决办法:C#问题 改成x86 不要选择anycpu

CentOS 7.9 安装详解:手动分区完全指南

CentOS 7.9 安装详解:手动分区完全指南 为什么需要手动分区?CentOS 7.9 基本分区说明1. /boot/efi 分区2. /boot 分区3. swap 交换分区4. / (根) 分区 可选分区(进阶设置)5. /home 分区6. /var 分区7. /tmp 分区 分区方案建议标准…

如何使用测试软件 Jmeter

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

2025盘古石初赛WP

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

系统分析与设计期末复习

第一章 系统的五个特性 整体性、目的性、相关性、环境适应性、层次性 软件系统的四个特性 复杂性、一致性、可变性、不可见性 第二章 系统规划 系统开发生命周期 系统规划->系统分析->系统设计->系统实施->系统运行维护->系统规划 诺兰阶段模型 阶段&a…

IBM BAW(原BPM升级版)使用教程第八讲

续前篇! 一、流程开发功能模块使用逻辑和顺序 前面我们已经对 流程、用户界面、公开的自动化服务、服务、事件、团队、数据、性能、文件各个模块进行了详细讲解,现在统一进行全面统一讲解。 在 IBM Business Automation Workflow (BAW) 中,…

从电动化到智能化,法雷奥“猛攻”中国汽车市场

当前,全球汽车产业正在经历前所未有的变革,外资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时代的可视化不仅能自动解读数据,还能预测…

深入理解 TCP:重传机制、滑动窗口、流量控制与拥塞控制

TCP(Transmission Control Protocol)是一个面向连接、可靠传输的协议,支撑着绝大多数互联网通信。在实现可靠性的背后,TCP 引入了多个关键机制:重传机制、滑动窗口、流量控制 和 拥塞控制。这些机制共同协作&#xff0…

uniapp-商城-51-后台 商家信息(logo处理)

前面对页面基本进行了梳理和说明,特别是对验证规则进行了阐述,并对自定义规则的兼容性进行了特别补充,应该说是干货满满。不知道有没有小伙伴已经消化了。 下面我们继续前进,说说页面上的logo上传组件,主要就是uni-fil…

springboot 加载 tomcat 源码追踪

加载 TomcatServletWebServerFactory 从 SpringApplication.run()方法进入 进入到 refresh () 方法 选择实现类 ServletWebServerApplicationContext 进入到 AbstractApplicationContext onRefresh() 方法创建容器 找到加载bean 得到 webServer 实例 点击 get…

使用countDownLatch导致的线程安全问题,线程不安全的List-ArrayList,线程安全的List-CopyOnWriteArrayList

示例代码 package com.example.demo.service;import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class UnSafeCDTest {Executor…