MySQL索引完全指南:让你的查询速度飞起来
MySQL索引完全指南:让你的查询速度飞起来
还在为数据库查询慢而头疼吗?一个简单的索引就能让你的查询速度提升几十倍甚至上百倍!今天我将用最通俗易懂的方式,带你彻底搞懂MySQL索引的奥秘。从什么是索引,到如何设计高效索引,再到实战优化技巧,让你从数据库小白变成查询优化高手!
一、索引是什么?为什么这么重要?
索引就像字典的目录
想象一下,你要在一本1000页的字典里找"程序员"这个词,你会怎么做?
- 没有目录:从第1页开始一页一页翻,可能要翻500页才能找到
- 有目录:直接翻到目录,找到"程"字开头的词在第300页,瞬间就找到了
数据库索引就是这样的"目录",它能帮我们快速定位数据的位置。
索引的神奇效果
| 场景 | 无索引 | 有索引 | 性能提升 |
|---|---|---|---|
| 100万条数据查询 | 扫描100万行 | 扫描3-4行 | 提升25万倍+ |
| 用户登录验证 | 50ms | 1ms | 提升50倍 |
| 订单查询 | 200ms | 5ms | 提升40倍 |
真实的例子
-- 没有索引的查询(慢得要命)
SELECT * FROM users WHERE email = 'john@example.com';
-- 执行时间:1.2秒(扫描了50万行数据)
-- 给email字段添加索引后
CREATE INDEX idx_email ON users(email);
SELECT * FROM users WHERE email = 'john@example.com';
-- 执行时间:0.01秒(直接定位到1行数据)
看到了吗?同样的查询,性能差了120倍!
二、索引的底层原理:B+树的魔法
什么是B+树?
不要被这个名字吓到,B+树其实很好理解。想象一下一个倒置的大树:
A[根节点: 50, 100] --> B[叶子节点: 1-50]
A --> C[叶子节点: 51-100]
A --> D[叶子节点: 101-150]
B --> E[数据: 1,2,3...50]
C --> F[数据: 51,52,53...100]
D --> G[数据: 101,102,103...150]
B+树的查找过程
让我们用一个简单例子来理解:
-- 假设我们要查找 id = 75 的用户
SELECT * FROM users WHERE id = 75;
查找步骤:
- 第1步:从根节点开始,75在50-100之间,走中间分支
- 第2步:到达叶子节点,找到id=75的数据位置
- 第3步:根据位置直接获取完整的用户数据
整个过程只需要3次磁盘IO,而全表扫描可能需要几万次!
为什么B+树这么快?
| 特点 | 优势 | 实际效果 |
|---|---|---|
| 多路平衡 | 树的高度很低 | 减少磁盘访问次数 |
| 叶子节点连接 | 支持范围查询 | ORDER BY、分页查询快 |
| 只在叶子存数据 | 内部节点小 | 更多索引数据放入内存 |
三、MySQL索引的类型详解
1. 主键索引(Primary Key)
主键索引是最特殊的索引,它就像身份证号码一样:
-- 创建主键索引
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT, -- 自动创建主键索引
name VARCHAR(50),
email VARCHAR(100)
);
-- 主键查询超级快
SELECT * FROM users WHERE id = 12345; -- 毫秒级响应
主键索引的特点:
- 唯一且不能为空
- 一个表只能有一个主键
- 查询性能最好
- 数据按主键顺序存储
2. 唯一索引(Unique Index)
-- 给邮箱添加唯一索引
CREATE UNIQUE INDEX idx_email ON users(email);
-- 插入重复邮箱会报错
INSERT INTO users(name, email) VALUES('张三', 'test@qq.com'); -- 成功
INSERT INTO users(name, email) VALUES('李四', 'test@qq.com'); -- 失败,邮箱重复
3. 普通索引(Normal Index)
最常用的索引类型:
-- 给姓名添加普通索引
CREATE INDEX idx_name ON users(name);
-- 快速查找用户
SELECT * FROM users WHERE name = '张三';
4. 复合索引(Composite Index)
多个字段组合的索引,功能更强大:
-- 创建复合索引
CREATE INDEX idx_name_age_city ON users(name, age, city);
-- 这些查询都能用到索引
SELECT * FROM users WHERE name = '张三'; -- ✓ 能用到
SELECT * FROM users WHERE name = '张三' AND age = 25; -- ✓ 能用到
SELECT * FROM users WHERE name = '张三' AND age = 25 AND city = '北京'; -- ✓ 能用到
SELECT * FROM users WHERE age = 25; -- ✗ 用不到
SELECT * FROM users WHERE city = '北京'; -- ✗ 用不到
复合索引的使用规则(最左前缀原则):
-- 索引:(name, age, city)
-- 可以理解为创建了三个索引:
-- 1. (name)
-- 2. (name, age)
-- 3. (name, age, city)
四、索引设计的黄金法则
法则1:为WHERE条件添加索引
-- 经常这样查询
SELECT * FROM orders WHERE user_id = 123;
SELECT * FROM orders WHERE status = 'paid';
SELECT * FROM orders WHERE create_time > '2024-01-01';
-- 就应该创建这些索引
CREATE INDEX idx_user_id ON orders(user_id);
CREATE INDEX idx_status ON orders(status);
CREATE INDEX idx_create_time ON orders(create_time);
法则2:为ORDER BY字段添加索引
-- 经常按创建时间排序
SELECT * FROM articles ORDER BY create_time DESC LIMIT 10;
-- 创建索引让排序飞快
CREATE INDEX idx_create_time ON articles(create_time);
法则3:复合索引的顺序很关键
-- 如果经常这样查询
SELECT * FROM users WHERE city = '北京' AND age > 25 ORDER BY create_time;
-- 索引字段顺序应该是:过滤性强的字段在前
CREATE INDEX idx_city_age_create_time ON users(city, age, create_time);
法则4:覆盖索引让查询更快
-- 如果只需要这几个字段
SELECT id, name, email FROM users WHERE age = 25;
-- 创建覆盖索引,连回表都省了
CREATE INDEX idx_age_name_email ON users(age, name, email);
五、实战案例:订单系统优化
场景描述
假设我们有一个订单表:
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
order_no VARCHAR(50) NOT NULL,
status ENUM('pending', 'paid', 'shipped', 'completed', 'cancelled'),
total_amount DECIMAL(10,2),
create_time DATETIME,
update_time DATETIME
);
常见查询场景及优化
场景1:用户查看自己的订单
-- 原始查询(慢)
SELECT * FROM orders WHERE user_id = 12345 ORDER BY create_time DESC;
-- 优化方案
CREATE INDEX idx_user_id_create_time ON orders(user_id, create_time);
优化效果:
- 优化前:扫描50万行数据,耗时800ms
- 优化后:直接定位用户订单,耗时5ms
场景2:订单状态查询
-- 查询待支付订单
SELECT * FROM orders WHERE status = 'pending' AND create_time > '2024-01-01';
-- 优化方案
CREATE INDEX idx_status_create_time ON orders(status, create_time);
场景3:订单号精确查找
-- 通过订单号查找
SELECT * FROM orders WHERE order_no = 'ORD20240101001';
-- 优化方案
CREATE UNIQUE INDEX idx_order_no ON orders(order_no);
优化前后对比
| 查询类型 | 优化前耗时 | 优化后耗时 | 提升倍数 |
|---|---|---|---|
| 用户订单查询 | 800ms | 5ms | 160倍 |
| 状态筛选 | 1200ms | 8ms | 150倍 |
| 订单号查找 | 600ms | 2ms | 300倍 |
六、索引的注意事项:别踩这些坑
坑1:不要给小表建索引
-- 错误示例:给只有100行数据的字典表建索引
CREATE TABLE dict_status (
id INT PRIMARY KEY,
name VARCHAR(20)
);
-- 这个表数据量太小,建索引反而浪费空间
坑2:不要在区分度低的字段建索引
-- 错误示例:性别字段只有男/女两个值
CREATE INDEX idx_gender ON users(gender); -- 没意义,区分度太低
坑3:索引不是越多越好
-- 错误示例:给每个字段都建索引
CREATE INDEX idx_name ON users(name);
CREATE INDEX idx_age ON users(age);
CREATE INDEX idx_city ON users(city);
CREATE INDEX idx_phone ON users(phone);
CREATE INDEX idx_email ON users(email);
-- 太多索引会严重影响INSERT/UPDATE性能
坑4:复合索引的字段顺序
-- 错误示例
CREATE INDEX idx_age_name ON users(age, name);
SELECT * FROM users WHERE name = '张三'; -- 用不到索引
-- 正确示例
CREATE INDEX idx_name_age ON users(name, age);
SELECT * FROM users WHERE name = '张三'; -- 能用到索引
七、索引优化实战技巧
技巧1:使用EXPLAIN分析查询
-- 分析查询是否使用了索引
EXPLAIN SELECT * FROM orders WHERE user_id = 12345;
EXPLAIN结果解读:
| 字段 | 说明 | 好的值 | 坏的值 |
|---|---|---|---|
| type | 访问类型 | const, eq_ref, ref | ALL, index |
| key | 使用的索引 | 有具体索引名 | NULL |
| rows | 扫描行数 | 越少越好 | 很大的数字 |
| Extra | 额外信息 | Using index | Using filesort |
技巧2:监控慢查询
-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- 超过1秒的查询记录下来
-- 查看慢查询
SHOW VARIABLES LIKE 'slow_query_log_file';
技巧3:定期分析表统计信息
-- 更新表的统计信息,让优化器做出更好的选择
ANALYZE TABLE orders;
技巧4:合理使用前缀索引
-- 对于很长的字符串字段,使用前缀索引
CREATE INDEX idx_title_prefix ON articles(title(20)); -- 只索引前20个字符
八、高级索引特性
1. 函数索引(MySQL 8.0+)
-- 给计算字段创建索引
ALTER TABLE orders ADD INDEX idx_year ((YEAR(create_time)));
-- 这个查询能用到索引
SELECT * FROM orders WHERE YEAR(create_time) = 2024;
2. 降序索引(MySQL 8.0+)
-- 创建降序索引
CREATE INDEX idx_create_time_desc ON orders(create_time DESC);
-- 降序排序更快
SELECT * FROM orders ORDER BY create_time DESC LIMIT 10;
3. 不可见索引
-- 创建不可见索引(用于测试)
CREATE INDEX idx_test ON orders(status) INVISIBLE;
-- 测试性能后再设为可见
ALTER INDEX idx_test VISIBLE;
九、索引维护:让索引保持最佳状态
定期检查索引使用情况
-- 查看索引使用统计
SELECT
schema_name,
table_name,
index_name,
rows_selected,
rows_inserted,
rows_updated,
rows_deleted
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE schema_name = 'your_database';
删除无用索引
-- 找出从未使用的索引
SELECT
t.table_schema,
t.table_name,
t.index_name
FROM information_schema.statistics t
LEFT JOIN performance_schema.table_io_waits_summary_by_index_usage p
ON t.table_schema = p.object_schema
AND t.table_name = p.object_name
AND t.index_name = p.index_name
WHERE p.index_name IS NULL
AND t.table_schema = 'your_database'
AND t.index_name != 'PRIMARY';
重建碎片化的索引
-- 检查索引碎片化程度
SHOW TABLE STATUS WHERE name = 'orders';
-- 重建索引
ALTER TABLE orders ENGINE=InnoDB;
十、实际项目中的索引策略
电商系统索引设计
-- 商品表
CREATE TABLE products (
id BIGINT PRIMARY KEY,
category_id INT,
name VARCHAR(200),
price DECIMAL(10,2),
stock INT,
status TINYINT,
create_time DATETIME,
-- 核心索引
INDEX idx_category_status_price (category_id, status, price),
INDEX idx_name (name),
INDEX idx_create_time (create_time)
);
-- 订单表
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id BIGINT,
status TINYINT,
total_amount DECIMAL(10,2),
create_time DATETIME,
-- 核心索引
INDEX idx_user_id_create_time (user_id, create_time),
INDEX idx_status_create_time (status, create_time)
);
社交系统索引设计
-- 用户关注表
CREATE TABLE user_follows (
id BIGINT PRIMARY KEY,
follower_id BIGINT, -- 关注者
following_id BIGINT, -- 被关注者
create_time DATETIME,
-- 核心索引
INDEX idx_follower_id (follower_id), -- 查询我关注的人
INDEX idx_following_id (following_id), -- 查询关注我的人
UNIQUE KEY uk_follow (follower_id, following_id) -- 防止重复关注
);
十一、性能测试与优化案例
案例1:用户登录优化
场景: 用户登录验证
-- 优化前的查询
SELECT id, password_hash FROM users WHERE email = 'user@example.com';
-- 性能测试结果
-- 数据量:100万用户
-- 查询时间:平均 850ms
-- 扫描行数:平均 50万行
优化方案:
-- 1. 创建邮箱唯一索引
CREATE UNIQUE INDEX idx_email ON users(email);
-- 2. 创建覆盖索引(避免回表)
CREATE INDEX idx_email_password ON users(email, password_hash);
优化效果:
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 查询时间 | 850ms | 2ms | 425倍 |
| 扫描行数 | 50万行 | 1行 | 50万倍 |
| CPU使用率 | 85% | 5% | 17倍 |
案例2:分页查询优化
场景: 商品列表分页查询
-- 优化前:传统分页(深度分页很慢)
SELECT * FROM products
WHERE category_id = 5
ORDER BY create_time DESC
LIMIT 50000, 20; -- 第2500页,超级慢
-- 优化后:游标分页
SELECT * FROM products
WHERE category_id = 5 AND create_time < '2024-01-15 10:30:00'
ORDER BY create_time DESC
LIMIT 20;
性能对比:
| 页数 | 传统分页 | 游标分页 | 性能提升 |
|---|---|---|---|
| 第1页 | 5ms | 3ms | 1.7倍 |
| 第100页 | 50ms | 3ms | 16.7倍 |
| 第1000页 | 500ms | 3ms | 166.7倍 |
| 第5000页 | 2500ms | 3ms | 833.3倍 |
十二、总结与最佳实践
索引设计的黄金原则
1. 基础原则:
- 主键索引是必须的
- 经常WHERE查询的字段要建索引
- 经常ORDER BY的字段要建索引
- 区分度高的字段适合建索引
2. 复合索引原则:
- 遵循最左前缀原则
- 区分度高的字段放在前面
- 经常组合查询的字段建复合索引
3. 性能原则:
- 索引不是越多越好
- 定期检查和清理无用索引
- 监控慢查询,及时优化
常见的索引使用误区
| 误区 | 说明 | 正确做法 |
|---|---|---|
| 给所有字段建索引 | 浪费空间,影响写性能 | 只给查询频繁的字段建索引 |
| 忽略复合索引顺序 | 索引失效 | 按最左前缀原则设计 |
| 不监控索引使用情况 | 存在无用索引 | 定期检查,清理无用索引 |
| 小表也建索引 | 得不偿失 | 小表(<1000行)不建议建索引 |
索引优化的完整流程
A[识别慢查询] --> B[分析查询模式]
B --> C[设计合适的索引]
C --> D[创建索引]
D --> E[测试性能效果]
E --> F{性能是否满足要求?}
F -->|否| G[调整索引设计]
F -->|是| H[部署上线]
G --> C
H --> I[持续监控]
I --> J[定期优化]
记住,索引优化是一个持续的过程,需要根据业务的发展不断调整和优化。一个好的索引设计能让你的数据库性能提升几十倍甚至上百倍,这就是索引的魅力所在!
掌握了这些索引知识,你就能让数据库查询速度飞起来,从此告别慢查询的烦恼。记住:好的索引设计 = 更快的查询 = 更好的用户体验 = 更成功的产品!
想要学习更多数据库优化技巧和实战经验?欢迎关注我的微信公众号【一只划水的程序猿】,这里有最实用的技术干货和最接地气的编程技巧,让你的技术水平快速提升!记得点赞收藏,分享给更多需要的小伙伴!
MySQL索引完全指南:让你的查询速度飞起来的更多相关文章
- mysql处理海量数据时的一些优化查询速度方法
最近一段时间由于工作需要,开始关注针对Mysql数据库的select查询语句的相关优化方法. 由于在参与的实际项目中发现当mysql表的数据量达到百万级时,普通SQL查询效率呈直线下降,而且如果w ...
- Mysql处理海量数据时的一些优化查询速度方法(转)
最近一段时间由于工作需要,开始关注针对Mysql数据库的select查询语句的相关优化方法. 由于在参与的实际项目中发现当mysql表的数据量达到百万级时,普通SQL查询效率呈直线下降,而且如果whe ...
- Mysql处理海量数据时的一些优化查询速度方法【转】
最近一段时间由于工作需要,开始关注针对Mysql数据库的select查询语句的相关优化方法.由于在参与的实际项目中发现当mysql表的数据量达到百万级时,普通SQL查询效率呈直线下降,而且如果wher ...
- MySQL 处理海量数据时的一些优化查询速度方法
查询速度慢的原因 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O 吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 5.网络速度慢 6 ...
- 通过非聚集索引让select count(*) from 的查询速度提高几十倍、甚至千倍
通过非聚集索引,可以显著提升count(*)查询的性能. 有的人可能会说,这个count(*)能用上索引吗,这个count(*)应该是通过表扫描来一个一个的统计,索引有用吗? 不错,一般的查询,如果用 ...
- C# Mysql Dapper和原生sql 插入和查询速度比较
1.表中有三个字段,已经有100多万条数据,每次插入10万条数据 时间单位:秒 秒 Dapper批量Model插入时间:40.6165513,Dapper单条Model插入时间:95.9492972, ...
- mysql字段的适当冗余有利于提高查询速度
CREATE TABLE `comment` ( `c_id` int(11) NOT NULL auto_increment COMMENT '评论ID', `u_id` int(11) NOT ...
- mysql索引的使用[上]
数据库的explain关键字和联合索引优化: 本篇文章简单的说一下mysql查询的优化以及explain语句的使用.(新手向) 因为这篇文章是面向查询的,直观一点,首先我们创建一个表:student ...
- MySQL索引优化看这篇文章就够了!
阅读本文大概需要 5 分钟. 来源:cnblogs.com/songwenjie/p/9410009.html 本文主要讨论MySQL索引的部分知识.将会从MySQL索引基础.索引优化实战和数据库索引 ...
- Mysql索引引起的死锁
提到索引,首先想到的是效率提高,查询速度提升,不知不觉都会有一种心理趋向,管它三七二十一,先上个索引提高一下效率..但是索引其实也是暗藏杀机的... 今天压测带优化项目,开着Jmeter高并发访问项目 ...
随机推荐
- TCP协议详细介绍
TCP报文格式: 字段介绍: 源/目的端口:用来标识主机上的程序 序号(seq):4个byte,指当前tcp报文段中第一个字节的序号(tcp报文中每个字节都有一个编号) 确认号(ack):4个byte ...
- 前端必备的 CSS 库,normalize.css
这是一个小 CSS 样式表,是著名的库,作为 CSS 基础样式的一部分,可消除客户端渲染不一致问题. 地址是 https://necolas.github.io/normalize.css/ 别小看这 ...
- static修饰成员方法、static修饰成员的特点总结、浅聊主方法-java se进阶 day01
1.工具类的介绍 工具类不是用于描述事物的类,而是帮我们完成事情的类(打工) 如图 当我们编写完这个类后,我们会发现一件事,这个类自己本身并没有意义,这个类完全是给用户进行调用方法的 既然是专门给用户 ...
- 请运行命令来卸载Oracle主目录
卸载Oracle数据库碰见的一个不一样的操作,留爪. 正常点的软件卸载直接点卸载即可, Oracle 卸载非要来点不一样警告, 如图: 注意:不要被图里的斜杠符号忽略了,准确的应该是: # 注意斜杠 ...
- SQL语句执行慢情况
排查历史慢查询: SELECT TOP 20 [Total IO] = (qs.total_logical_reads + qs.total_logical_writes) , [Average IO ...
- DevExpress汉化
//ini 汉化文件的使用方法: var cxLocalizer1: TcxLocalizer; begin cxLocalizer1.FileName := '你的路径\DevChs.ini'; c ...
- Windows 左ctrl和左alt键互换
reg代码 Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyb ...
- 题解:AT_abc369_d [ABC369D] Bonus EXP
题目大意: 有 nnn 个怪物,每个怪物有一个战力值 aia_iai ,你可以选择击败他或放走他,放走他没有经验值,击败他可以获得 aia_iai 的经验值,如果击败的数量是偶数,则还可以获得 a ...
- 基础 DP 做题记录
Luogu P1192 台阶问题 Link 简要题意: 给定台阶数 \(n\le10^5\) 和一步至多跨越台阶数 \(k\le10^2\) ,初始在 \(0\) 级,求方案数 \(\pmod {10 ...
- heapdump敏感信息提取工具-heapdump_tool(二),附下载链接。
heapdump敏感信息查询工具,例如查找 spring heapdump中的密码明文,AK,SK等 下载链接: heapdump_tool下载链接:heapdump_tool下载 声明: 此工具 ...