MySQL忘记root密码重置(Linux环境)
前言
做运维或者开发的同学肯定遇到过这种情况:服务器上的MySQL数据库用了很久,突然有一天忘记了root密码,登录不进去了。这时候项目可能正在运行,不能随便重装数据库,那怎么办呢?
别慌,MySQL提供了一种跳过权限验证的启动方式,我们可以用这种方式登录进去,然后重置root密码。这篇文章就详细讲讲在Linux环境下怎么重置MySQL的root密码。
操作步骤
1. 先停止MySQL服务
首先我们需要把正在运行的MySQL服务停掉:
systemctl stop mysqld
如果你用的是MariaDB,命令是:
systemctl stop mariadb
执行完之后可以检查一下MySQL是不是真的停了:
ps aux | grep mysql
如果没有看到mysql进程,说明已经停掉了。
2. 跳过权限验证启动MySQL
接下来我们要用 --skip-grant-tables 参数启动MySQL,这样启动之后就可以不用密码直接登录了。
mysqld_safe --skip-grant-tables &
注意最后面的 & 符号,这个是让MySQL在后台运行。
执行完这个命令之后,等个几秒钟,让MySQL完全启动起来。
3. 无密码登录MySQL
现在我们就可以不用密码直接登录MySQL了:
mysql -u root
执行完之后直接就进入MySQL命令行界面了,不需要输入密码。
4. 修改root密码
登录进去之后,我们就可以修改root密码了。
首先选择mysql数据库:
USE mysql;
然后修改root密码。注意不同版本的MySQL,修改密码的语句不一样:
MySQL 5.7版本:
UPDATE user SET authentication_string=PASSWORD('你的新密码') WHERE User='root';
MySQL 8.0版本:
ALTER USER 'root'@'localhost' IDENTIFIED BY '你的新密码';
修改完密码之后,一定要执行一下刷新权限的命令:
FLUSH PRIVILEGES;
然后退出MySQL:
EXIT;
5. 关闭跳过权限的MySQL进程,正常重启MySQL
现在我们需要把刚才用跳过权限方式启动的MySQL进程关掉,然后用正常方式重启。
先找到MySQL的进程ID:
ps aux | grep mysqld_safe
然后杀掉这个进程:
kill -9 进程ID
等个几秒钟,让MySQL完全关闭。然后用正常方式启动MySQL:
systemctl start mysqld
6. 验证新密码能不能登录
最后我们来验证一下新密码能不能正常登录:
mysql -u root -p
输入你刚才设置的新密码,如果能正常登录进去,说明密码重置成功了。
常见坑
坑1:修改密码之后忘了执行FLUSH PRIVILEGES
很多同学修改完密码之后直接就退出了,结果发现重启MySQL之后密码还是不对。这是因为修改完密码之后一定要执行 FLUSH PRIVILEGES 刷新权限,不然修改不会生效。
坑2:MySQL版本不一样,修改密码的语句不一样
很多同学抄网上的教程,结果修改密码的语句不对,报错了。这是因为MySQL 5.7和MySQL 8.0修改密码的方式不一样。5.7用的是 authentication_string 字段,8.0用的是 ALTER USER 语句。
你可以先执行一下这个命令看看你的MySQL版本:
SELECT VERSION();
然后用对应的语句修改密码。
坑3:跳过权限启动的时候没加 & 符号
如果执行 mysqld_safe --skip-grant-tables 的时候没加 & 符号,那这个命令就会一直占着你的终端窗口,你没法继续操作了。所以一定要记得在最后面加个 & 符号,让它在后台运行。
坑4:杀进程的时候杀错了
杀进程的时候一定要看清楚,不要把其他无关的进程杀掉了。最好先 ps aux | grep mysqld 看一下,确认哪个是我们刚才启动的跳过权限的MySQL进程。
总结
Linux环境下重置MySQL root密码的步骤总结一下:
- 停止MySQL服务
- 用 --skip-grant-tables 参数跳过权限启动
- 无密码登录MySQL
- 修改root密码
- 刷新权限
- 关闭跳过权限的进程,正常重启MySQL
- 用新密码验证登录
记住这几个步骤,以后忘记root密码的时候就不用慌了,按照这个流程操作就能搞定。
遇到问题加QQ23979811 协助处理
MySQL慢查询开启,定位慢SQL语句方法
前言
做网站开发的同学肯定遇到过这种情况:网站打开越来越慢,数据库CPU占用很高,但就是不知道哪条SQL语句拖慢了整个系统。这时候就需要用到MySQL的慢查询日志功能,把执行慢的SQL语句记录下来,然后逐个优化。
这篇文章就详细讲讲如何开启MySQL慢查询日志,以及怎么用它来定位和优化慢SQL语句。
操作步骤
1. 先查看当前慢查询日志的状态
登录MySQL之后,先看看慢查询日志有没有开启:
SHOW VARIABLES LIKE '%slow_query_log%';
执行完之后会看到类似下面的输出:
+---------------------+-----------------------------------------------+
| Variable_name | Value |
+---------------------+-----------------------------------------------+
| slow_query_log | OFF |
| slow_query_log_file | /var/lib/mysql/izuf6w7x1x2x3x4x5x6x-slow.log |
+---------------------+-----------------------------------------------+
从这里可以看到,slow_query_log 是 OFF 状态,说明慢查询日志没有开启。
2. 查看当前慢查询时间阈值
慢查询的默认时间阈值是10秒,也就是说执行时间超过10秒的SQL才会被记录。这个阈值对生产环境来说太大了,一般我们会改成1秒或者2秒。
查看当前阈值:
SHOW VARIABLES LIKE 'long_query_time';
3. 临时开启慢查询日志(重启后失效)
如果只是想临时排查问题,可以直接在MySQL命令行里开启,不需要重启服务:
-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
-- 设置慢查询阈值为1秒
SET GLOBAL long_query_time = 1;
这样设置之后,执行时间超过1秒的SQL语句就会被记录到慢查询日志文件里了。
4. 永久开启慢查询日志(修改配置文件)
临时开启的方式在MySQL重启之后就会失效,如果想长期开启,需要修改MySQL的配置文件。
找到MySQL的配置文件,一般是 /etc/my.cnf 或者 /etc/mysql/my.cnf,在 [mysqld] 下面添加以下配置:
[mysqld]
# 开启慢查询日志
slow_query_log = 1
# 慢查询日志文件位置
slow_query_log_file = /var/log/mysql/slow.log
# 慢查询阈值,单位秒
long_query_time = 1
# 没有用到索引的SQL也记录下来
log_queries_not_using_indexes = 1
修改完配置文件之后,重启MySQL服务:
systemctl restart mysqld
5. 查看慢查询日志内容
慢查询日志开启之后,执行慢的SQL语句就会被记录到日志文件里。我们可以直接查看日志文件:
cat /var/log/mysql/slow.log
不过慢查询日志的格式比较复杂,直接看日志文件不太直观。我们可以用 mysqldumpslow 工具来分析:
# 查看最慢的10条SQL
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# 查看访问次数最多的10条SQL
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log
6. 使用EXPLAIN分析慢SQL
找到慢SQL之后,下一步就是分析这条SQL为什么慢。我们可以用 EXPLAIN 关键字来查看SQL的执行计划:
EXPLAIN SELECT * FROM users WHERE name = '张三';
执行完之后会输出很多字段,我们重点关注这几个:
- type:访问类型,最好是 const、eq_ref,最差是 ALL(全表扫描)
- key:实际用到的索引,如果是 NULL 说明没用到索引
- rows:扫描的行数,这个值越大越慢
- Extra:额外信息,如果看到 Using filesort 或者 Using temporary 就说明需要优化了
常见坑
坑1:开启了慢查询但日志文件里什么都没有
很多同学开启了慢查询日志,但是去看日志文件却发现什么都没有。这通常是因为以下几个原因:
- 慢查询时间阈值设置得太大了,你的SQL执行时间还没超过阈值
- 日志文件的路径不对,你看的不是正确的日志文件
- MySQL没有权限写入日志文件,需要检查文件权限
坑2:long_query_time设置了但不生效
有时候你设置了 long_query_time = 1,但是发现执行时间0.5秒的SQL也被记录下来了。这是因为当前已经建立的MySQL连接还是用的旧的阈值,需要重新连接MySQL才能生效。
坑3:慢查询日志文件太大占满磁盘
如果你的网站访问量很大,慢查询日志可能会增长得非常快,几天就把磁盘占满了。所以一定要配置日志切割,或者定期清理慢查询日志文件。
总结
MySQL慢查询日志是定位数据库性能问题的必备工具,核心就三步:
- 开启慢查询日志,设置合理的时间阈值(一般1秒)
- 用mysqldumpslow工具分析慢查询日志,找到最慢的SQL
- 用EXPLAIN分析慢SQL的执行计划,针对性优化(加索引、改写SQL等)
掌握了慢查询日志的使用方法,网站数据库性能问题就能快速定位和解决了。
遇到问题加QQ23979811 协助处理
MySQL 索引优化技巧
MySQL 索引优化技巧
索引是数据库性能的关键,加对了查询快十倍,加错了反而更慢。
1. 索引类型
主键索引:主键自带索引,一张表只能一个。
普通索引:最基本的索引,加速查询。
CREATE INDEX idx_name ON user(name);
唯一索引:字段值不能重复。
CREATE UNIQUE INDEX idx_email ON user(email);
联合索引:多个字段组合的索引。
CREATE INDEX idx_name_age ON user(name, age);
2. 索引失效的情况
1)like 以 % 开头
-- 索引失效
SELECT * FROM user WHERE name LIKE '%张';
-- 索引生效
SELECT * FROM user WHERE name LIKE '张%';
2)对字段做函数运算
-- 索引失效
SELECT * FROM user WHERE LEFT(name,1) = '张';
-- 索引生效
SELECT * FROM user WHERE name LIKE '张%';
3)联合索引不满足最左前缀
联合索引 (name, age):
-- 生效
SELECT * FROM user WHERE name = '张三';
-- 生效
SELECT * FROM user WHERE name = '张三' AND age = 25;
-- 失效
SELECT * FROM user WHERE age = 25;
4)隐式类型转换
-- phone 是 varchar
-- 索引失效
SELECT * FROM user WHERE phone = 13800001111;
-- 索引生效
SELECT * FROM user WHERE phone = '13800001111';
3. 查看索引使用情况
-- 查看执行计划
EXPLAIN SELECT * FROM user WHERE name = '张三';
-- 重点看 type 字段:
-- system > const > eq_ref > ref > range > index > ALL
-- 最差是 ALL(全表扫描)
4. 索引优化原则
- 为高频查询加索引:不要乱加
- 区分度高的字段优先:性别这种区分度低的不加
- 联合索引把最常用的放左边
- 索引不是越多越好:增删改会变慢
- 避免索引冗余:(a,b) 包含了 (a)
5. 慢查询排查
-- 开启慢查询
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2;
-- 查看慢查询日志
SHOW VARIABLES LIKE 'slow_query_log_file';
总结
MySQL 索引优化核心:
- 联合索引满足最左前缀
- like 不要以 % 开头
- 不要对字段做函数运算
- 用 EXPLAIN 看执行计划
- 慢查询找出来再优化
搞懂这些,数据库性能提升一大截。
MySQL 事务隔离级别
MySQL 事务隔离级别详解
事务是数据库并发控制的基础,搞懂四个隔离级别,才能解决脏读、幻读问题。
1. 事务 ACID 特性
- 原子性(Atomicity):要么全成功,要么全回滚
- 一致性(Consistency):事务前后数据一致
- 隔离性(Isolation):事务之间互不干扰
- 持久性(Durability):提交后永久保存
2. 四种隔离级别
MySQL 有四种隔离级别,从上到下越来越严格:
读未提交(READ UNCOMMITTED)
- 可以读到别人还没提交的数据
- 会出现脏读
- 几乎没人用
读提交(READ COMMITTED)
- 只能读到别人已经提交的数据
- 解决了脏读,但会出现不可重复读
- Oracle、SQL Server 默认级别
可重复读(REPEATABLE READ)
- 同一个事务里,读多次结果都一样
- 解决了脏读、不可重复读
- MySQL InnoDB 默认级别
- InnoDB 用间隙锁解决了幻读
串行化(SERIALIZABLE)
- 事务一个一个排队执行
- 最安全,但性能最差
- 几乎没人用
3. 三种读问题
脏读:读到了别人还没提交的数据,别人回滚了,你读到的就是脏数据。
不可重复读:同一个事务里,两次读同一行数据,结果不一样(别人中间改了并提交了)。
幻读:同一个事务里,两次查同一个范围,行数不一样(别人中间插了新行)。
4. 查看和设置隔离级别
-- 查看当前隔离级别
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
-- 设置全局隔离级别
SET GLOBAL tx_isolation = 'READ-COMMITTED';
-- 设置当前会话隔离级别
SET SESSION tx_isolation = 'READ-COMMITTED';
5. 实际应用建议
- 默认就好:MySQL 默认的可重复读已经够用
- 高并发场景:可以降到读提交,提升并发性能
- 金融级场景:用串行化,绝对安全
- 大多数业务:默认级别就行,不用折腾
6. 代码示例
// PDO 事务示例
try {
$pdo->beginTransaction();
$pdo->exec("UPDATE account SET money = money - 100 WHERE id = 1");
$pdo->exec("UPDATE account SET money = money + 100 WHERE id = 2");
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
echo "转账失败:" . $e->getMessage();
}
总结
MySQL 事务隔离级别核心:
- 读未提交:脏读
- 读提交:不可重复读
- 可重复读:默认级别,解决幻读
- 串行化:最安全最慢
大多数业务用默认的可重复读就够了。
MySQL 锁机制详解
MySQL 锁机制详解
MySQL 锁是并发控制的核心,搞懂锁,才能避免死锁和性能问题。
1. 表锁 vs 行锁
表锁:锁整个表,开销小,并发低。
- MyISAM 用的就是表锁
- 适合读多写少的场景
行锁:锁单行数据,开销大,并发高。
- InnoDB 用的就是行锁
- 适合写多的场景
2. 共享锁 vs 排他锁
共享锁(S锁):读锁,多个读不互斥。
SELECT ... LOCK IN SHARE MODE
排他锁(X锁):写锁,一个写独占。
SELECT ... FOR UPDATE
3. 间隙锁
InnoDB 用间隙锁防止幻读。锁的是一个范围,不是具体行。
比如你查 id > 10 AND id < 20,间隙锁会把这个区间锁住,别人插不进来。
4. 死锁
两个事务互相等对方的锁,都不放,就死锁了。
避免死锁的方法:
- 按相同顺序访问表
- 事务尽量小,快速提交
- 降低隔离级别
- 加索引,减少行锁范围
5. 查看锁状态
-- 查看当前锁
SHOW OPEN TABLES WHERE In_use > 0;
-- 查看 InnoDB 锁
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
-- 查看锁等待
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
6. 行锁什么时候会变成表锁
行锁失效就会升级成表锁:
- 没用索引
- 索引失效
- 全表扫描
所以加索引不仅是为了查得快,也是为了锁得准。
总结
MySQL 锁的核心:
- InnoDB 默认行锁,MyISAM 是表锁
- 共享锁读、排他锁写
- 间隙锁防幻读
- 死锁要避免:顺序访问、小事务、加索引
搞懂这些,数据库并发问题基本能解决一半。
MySQL 慢查询优化技巧
MySQL 慢查询优化实战技巧
数据库慢查询是生产环境最常见的性能问题之一。掌握慢查询优化技巧,能让系统性能提升数倍。
1. 开启慢查询日志
先开启慢查询日志,找出哪些 SQL 慢:
slow_query_log = 1
long_query_time = 1
slow_query_log_file = /var/log/mysql/slow.log
超过1秒的查询就会被记录。
2. 用 Explain 分析
慢 SQL 拿到后,用 EXPLAIN 分析执行计划:
EXPLAIN SELECT * FROM users WHERE name = '张三';
重点看:
- type:访问类型,ALL 是全表扫描,要优化
- key:实际用到的索引
- rows:扫描行数
- Extra:额外信息,Using filesort、Using temporary 要警惕
3. 加索引
最直接的优化就是加索引:
ALTER TABLE users ADD INDEX idx_name(name);
加索引原则:
- WHERE、JOIN、ORDER BY 字段优先
- 区分度高的字段效果好
- 联合索引遵循最左前缀
4. 避免 SELECT *
只查需要的字段:
-- 不好
SELECT * FROM users WHERE id = 1;
-- 好
SELECT id, name FROM users WHERE id = 1;
5. 深分页优化
LIMIT 100000, 10 很慢,因为要扫描10万行:
-- 不好
SELECT * FROM users LIMIT 100000, 10;
-- 好,用子查询
SELECT * FROM users
WHERE id >= (SELECT id FROM users LIMIT 100000, 1)
LIMIT 10;
6. 大表拆小
单表数据量超过千万行,性能会下降。可以:
- 按时间分表
- 按用户ID分库
- 归档历史数据
7. 总结
慢查询优化的核心思路:先找慢 SQL,再分析执行计划,然后加索引或改写 SQL。优化完记得对比前后效果,确保真的变快了。
MySQL 索引原理详解
MySQL 索引为什么能提速
索引是数据库性能优化的核心,搞懂原理才能用对。
1. 为什么慢查询慢
没有索引,MySQL 就得全表扫描,一行一行找。100万行数据就得扫100万次,当然慢。
有了索引,就像书有目录,直接定位到位置,查几百万行也很快。
2. B+ 树索引
MySQL InnoDB 默认用 B+ 树做索引。
- 树的高度低,一般3-4层
- 一次查询只需3-4次磁盘IO
- 叶子节点存数据,非叶子节点存索引
3. 聚簇索引 vs 二级索引
聚簇索引:数据和索引放在一起,按主键组织。一张表只有一个聚簇索引。
二级索引:索引和数据分开,叶子节点存主键值。查二级索引需要回表查主键。
4. 覆盖索引
如果查询的字段都在索引里,就不用回表了,叫覆盖索引。
-- name有索引
SELECT name FROM users WHERE name = '张三';
-- 覆盖索引,不用回表
5. 最左前缀
联合索引 (a, b, c),必须从最左列开始用:
WHERE a = 1能用WHERE a = 1 AND b = 2能用WHERE b = 2用不了
6. 索引失效场景
- 索引字段做函数运算
- 隐式类型转换
- like 以 % 开头
- 联合索引不满足最左前缀
总结
索引是空间换时间。不是越多越好,太多索引会影响写入性能,占用额外存储空间。
MySQL 8.0 新特性
MySQL 8.0 有哪些重要更新
MySQL 8.0 是目前最主流的大版本,相比 5.7 提升很大。
1. 性能提升
8.0 的性能比 5.7 提升约 2 倍,尤其是读取和写入并发场景下提升明显。
2. 原子 DDL
8.0 支持原子 DDL,建表、删表操作要么全成功要么全失败,不会出现一半成功一半失败的情况。
3. 窗口函数
支持窗口函数,比如 ROW_NUMBER()、RANK(),做排名统计很方便:
SELECT name, score,
RANK() OVER (ORDER BY score DESC) as rank
FROM students;
4. CTE 公共表表达式
支持 WITH 语法,复杂 SQL 写起来更清晰:
WITH top_users AS (
SELECT id, name FROM users WHERE vip = 1
)
SELECT * FROM top_users;
5. JSON 增强
JSON 操作函数更丰富,性能也更好,基本可以替代 MongoDB 的简单场景。
6. 隐藏索引
可以把索引设为隐藏状态,不删除但暂时不用,用来测试索引是否真的有用。
7. 字符集变化
默认字符集从 latin1 改成了 utf8mb4,支持 Emoji 表情。
总结
MySQL 8.0 是目前新项目的首选,性能好、特性全。老项目从 5.7 升级要注意兼容性,尤其是 SQL 模式和函数变化。
MySQL 备份与恢复:mysqldump 实战指南
前言
数据库备份是生产环境的生命线。很多人没出事的时候不备份,出事了才后悔。本文讲清楚 MySQL 最常用的备份工具 mysqldump。
一、为什么要备份
- 误操作:手滑删了表、删了数据
- 服务器挂了:硬盘坏了、机器报废
- 被攻击: ransomware 加密了数据库
- 升级失败:新版本不兼容,数据回退
记住:没有备份的数据库,就是在裸奔。
二、mysqldump 是什么
mysqldump 是 MySQL 自带的逻辑备份工具,把数据导出成 SQL 文件。
优点:简单、通用、跨版本
缺点:大数据量慢
适合:中小数据库(< 50G)
三、备份数据库
1. 备份整个数据库
mysqldump -u root -p 数据库名 > backup.sql
输入密码后,就会生成 backup.sql 文件。
2. 备份所有数据库
mysqldump -u root -p --all-databases > all_backup.sql
3. 备份单个表
mysqldump -u root -p 数据库名 表名 > table_backup.sql
4. 备份多个表
mysqldump -u root -p 数据库名 表1 表2 > multi_backup.sql
四、恢复数据库
1. 恢复整个数据库
mysql -u root -p 数据库名 < backup.sql
注意:数据库要先存在。
CREATE DATABASE 数据库名;
2. 恢复所有数据库
mysql -u root -p < all_backup.sql
五、常用参数
--single-transaction
InnoDB 引擎下,保证备份一致性,不锁表:
mysqldump -u root -p --single-transaction 数据库名 > backup.sql
生产环境一定要加这个!
--routines
备份存储过程和函数:
mysqldump -u root -p --routines 数据库名 > backup.sql
--triggers
备份触发器:
mysqldump -u root -p --triggers 数据库名 > backup.sql
--events
备份事件调度器:
mysqldump -u root -p --events 数据库名 > backup.sql
--no-data
只备份表结构,不备份数据:
mysqldump -u root -p --no-data 数据库名 > schema.sql
--where
按条件备份:
mysqldump -u root -p 数据库名 表名 --where="id > 100" > partial.sql
六、完整备份命令
生产环境推荐用这个:
mysqldump -u root -p \
--single-transaction \
--routines \
--triggers \
--events \
--default-character-set=utf8mb4 \
数据库名 > backup.sql
七、压缩备份
备份文件太大?压缩一下:
# 备份并压缩
mysqldump -u root -p 数据库名 | gzip > backup.sql.gz
# 解压并恢复
gunzip < backup.sql.gz | mysql -u root -p 数据库名
八、定时备份
写个脚本每天自动备份:
#!/bin/bash
# 备份目录
BACKUP_DIR="/data/backup/mysql"
DATE=$(date +%Y%m%d)
# 备份
mysqldump -u root -p密码 --single-transaction 数据库名 | gzip > $BACKUP_DIR/db_$DATE.sql.gz
# 删除 7 天前的备份
find $BACKUP_DIR -name "db_*.sql.gz" -mtime +7 -delete
加到 crontab:
# 每天凌晨 2 点备份
0 2 * * * /bin/bash /path/to/backup.sh
九、常见坑
- 备份不锁表:大表备份会锁表,一定要加 --single-transaction
- 字符集不对:备份文件乱码,加 --default-character-set=utf8mb4
- 没备份存储过程:导出后丢了,加 --routines
- 备份没验证:备份完一定要试恢复,不然真出事的时候哭都来不及
十、备份策略
- 每天全量备份:小数据库
- 每周全量 + 每天增量:大数据库
- 异地备份:备份文件要存到别的服务器
- 定期演练恢复:每个月测一次能不能恢复
总结
MySQL 备份的核心要点:
- mysqldump 是最常用的备份工具
- 生产环境加 --single-transaction,不锁表
- 备份要压缩,节省空间
- 定时备份,自动删除旧备份
- 备份一定要验证,能恢复才算备份成功
数据是公司最值钱的资产,备份这事儿千万别偷懒。
MySQL 索引优化实战:从 Explain 分析到查询性能翻倍
前言
数据库查询慢是 Web 开发中最常见的性能问题之一。而绝大多数慢查询都可以通过合理使用索引来解决。本文将从 Explain 执行计划入手,带你一步步掌握 MySQL 索引优化的实战技巧。
一、认识 Explain 执行计划
要优化查询,首先得知道 MySQL 是怎么执行你的 SQL 的。EXPLAIN 命令可以展示 MySQL 执行查询的详细步骤:
EXPLAIN SELECT * FROM orders WHERE user_id = 10086 AND status = 'paid';
重点关注以下几个字段:
- type:访问类型,从好到差依次为
const > eq_ref > ref > range > index > ALL。出现ALL就是全表扫描,必须优化。 - key:实际使用的索引。如果为 NULL,说明没有用到索引。
- rows:预估需要扫描的行数。这个值越大越慢。
- Extra:额外信息。
Using filesort和Using temporary都是危险信号。
二、最左前缀原则
联合索引 (a, b, c) 的匹配规则:
-- 用到索引 a,b,c
SELECT * FROM t WHERE a = 1 AND b = 2 AND c = 3;
-- 用到索引 a,b
SELECT * FROM t WHERE a = 1 AND b = 2;
-- 只用到索引 a(中间断了)
SELECT * FROM t WHERE a = 1 AND c = 3;
-- 完全用不到索引
SELECT * FROM t WHERE b = 2 AND c = 3;
实战要点:把等值查询的列放在前面,范围查询的列放在后面。
三、常见索引失效场景
1. 对索引列使用函数或运算
-- 失效:对索引列用了函数
SELECT * FROM users WHERE DATE(created_at) = '2026-01-01';
-- 有效:改成范围查询
SELECT * FROM users WHERE created_at >= '2026-01-01' AND created_at < '2026-01-02';
2. 隐式类型转换
-- 失效:phone 是 varchar,用数字查
SELECT * FROM users WHERE phone = 13800138000;
-- 有效:加引号
SELECT * FROM users WHERE phone = '13800138000';
3. LIKE 以通配符开头
-- 失效
SELECT * FROM users WHERE name LIKE '%张%';
-- 有效
SELECT * FROM users WHERE name LIKE '张%';
四、实战优化案例
问题 SQL(扫描 10 万行,耗时 1.2 秒):
SELECT id, title, created_at FROM articles
WHERE category_id = 5 AND status = 'published'
ORDER BY created_at DESC LIMIT 20;
Explain 结果:type=index,rows=120000,Using filesort。
优化方案:创建联合索引
ALTER TABLE articles ADD INDEX idx_cat_status_time (category_id, status, created_at DESC);
优化后:type=ref,rows=20,Extra 无 filesort。查询时间从 1.2 秒降到 0.005 秒。
五、索引不是越多越好
索引虽然能加速查询,但也有代价:
- 写入变慢:每次 INSERT/UPDATE 都要维护索引
- 占用空间:索引也是要存磁盘的
- 优化器选错:索引太多可能让优化器犯迷糊
建议:单表索引数量控制在 5 个以内,避免重复索引和冗余索引。
总结
MySQL 索引优化的核心思路:
- 用
EXPLAIN看执行计划,找到慢的原因 - 遵循最左前缀原则设计联合索引
- 避免常见的索引失效写法
- 定期清理无用索引
掌握这些技巧,你就能解决 80% 以上的慢查询问题。