MySQL性能优化入门指南:从慢查询日志到索引建立 (实战教程)

MySQL性能优化入门指南:从慢查询日志到索引建立 (实战教程)

你的网站,最近是不是也开始变得“步履蹒跚”?页面加载需要转好几个圈,后台操作也感觉卡卡的。你打开htop,常常看到mysqld那个进程,“霸占”着CPU排行榜的前列,像一个气喘吁吁的马拉松选手,拖慢了整个系统的节奏。

你心里清楚,问题,就出在数据库身上。它,这个你应用的“中央大脑”,正在因为某些“思维混乱”的查询,而陷入严重的“内耗”。

但问题是,这个“罪魁祸首”的查询,到底是哪一条?它藏在你成千上万行代码的哪个角落里?

今天,我将授予你两件“神器”:一把能让你清晰地看到“大脑”中所有“低效思绪”的“思维放大镜”(慢查询日志),以及一把能给这些思绪,建立起“高速神经连接”的“精密手术刀”(索引优化)。


MySQL性能优化入门:慢查询日志的分析与索引优化

在我们开始“探案”之前,我们必须先理解,一个数据库查询,为什么会“慢”?

想象一下,你的数据库,是一座藏书亿万的巨型图书馆

  • 一张数据表,就是图书馆里的一个分区(比如,“用户”分区)。
  • 一行数据,就是书架上的一本

当一个程序,向数据库发起一次高效的查询时,比如SELECT * FROM users WHERE id = 123;,这就像一个读者,拿着一张写着精确索书号的卡片,来到图书馆。图书管理员(MySQL)会立刻根据“索引系统”(卡片目录),直奔某个书架的某个位置,把那本书拿出来。这个过程,快如闪电。

但,当一个“慢查询”发生时,场景就完全不同了。比如SELECT * FROM users WHERE email = 'hello@world.com';,并且email这一列,没有“索引”。

这,就像一个读者,对图书管理员说:“你好,我想找一本作者是‘张三’的书,但我不知道书名,也没有索书号。

这位可怜的图书管理员,唯一的办法,就是把“用户”这个分区里,成千上万、甚至上百万本书,一本一本地抽出来,翻开作者页,看看是不是“张三”,然后再放回去。

这个过程,我们称之为“全表扫描 (Full Table Scan)”。当你的用户表里有几百万本书时,你可以想象,这个“找书”的过程,会有多慢。而我们的图书管理员(MySQL),在这期间,几乎无法再为其他读者提供服务了。

我们的任务,就是找到所有让图书管理员“满头大汗”的“奇葩找书请求”,然后,为它们,制作一张张精准的“索引卡片”。

第一步:安装“探案监控”——开启慢查询日志

默认情况下,MySQL这位图书管理员,即使累得半死,也不会主动向你“抱怨”。我们需要给他装一个“工作日志记录仪”,让他把所有超过规定处理时间的“疑难杂症”,都悄悄地记录下来。

这个“记录仪”,就是慢查询日志 (Slow Query Log)

  1. 打开MySQL的“基因编辑器”——配置文件my.cnf 这个文件通常在/etc/mysql/my.cnf/etc/my.cnf。用sudo nano打开它。
  2. [mysqld]区块下,加入以下“监控指令”:

Ini, TOML

# 开启慢查询日志
slow_query_log = 1

# 指定日志文件的存放位置
slow_query_log_file = /var/log/mysql/mysql-slow.log

# 定义“多慢”才算“慢”,单位是秒。建议从2秒开始
long_query_time = 2

# 【专业玩家选项】记录那些没有使用索引的查询
log_queries_not_using_indexes = 1
  • log_queries_not_using_indexes:这是一个极其有用的“告密”开关!开启它之后,即使某个查询,因为数据量小而执行得很快(比如,在0.5秒内就完成了),但如果MySQL发现,它在找这本书时,依然是“一本一本翻”的(全表扫描),它也会把这个“潜在的隐患”,记录到我们的日志里。

3.重启MySQL服务,让“监控”生效

Bash

sudo systemctl restart mysql
  1. 请确保/var/log/mysql/这个目录存在,并且MySQL用户有权限写入)

好了,我们的“探案监控”,已经开始7×24小时不间断工作了。让你的网站,先去“自由飞翔”一两天,让这份日志,去“捕获”那些性能杀手。

第二步:分析“犯罪记录”——解读慢查询日志

一两天后,我们回到服务器,打开那份神秘的日志文件。

Bash

sudo tail -n 100 /var/log/mysql/mysql-slow.log

你可能会看到类似这样的“犯罪记录”:

# Time: 2025-08-27T12:30:05.123456Z
# User@Host: my_app_user[my_app_user] @ localhost []
# Query_time: 5.345678  Lock_time: 0.000123  Rows_sent: 1  Rows_examined: 1589432
SET timestamp=1661589005;
SELECT * FROM users WHERE email = 'someuser@example.com';

如何解读?

  • Query_time: 5.34...执行时间。这条查询,花了5.3秒!对于一个网站来说,这简直是“灾难级”的延迟。
  • Rows_sent: 1返回行数。最终,只返回了1条记录。
  • Rows_examined: 1589432检查行数。这是最关键的破案线索!为了找出那1本书,图书管理员,竟然翻了158万本书
  • SELECT ...:这就是那条“罪恶滔天”的SQL语句。

当日志文件很长时,手动去看,效率很低。我们可以使用MySQL自带的“案情分析专家”——mysqldumpslow命令。

Bash

# -s r: 按照“检查行数”(Rows_examined)来排序
# -t 10: 只显示最慢的10条
sudo mysqldumpslow -s r -t 10 /var/log/mysql/mysql-slow.log

这个工具,会自动帮你把相同的慢查询,进行分组、归类、排序,让你一眼就看到,谁才是那个最应该被“枪毙”的“性能惯犯”。

第三步:固定“证据链”——用EXPLAIN让MySQL亲口“招供”

我们已经锁定了“犯罪嫌疑人”(那条SELECT语句)。现在,我们要对它进行“审讯”,让MySQL亲口告诉我们,它在执行这条查询时,到底干了些什么“蠢事”。

这个“审讯”工具,就是EXPLAIN命令。

登录你的MySQL,把EXPLAIN这个词,加在你那条慢查询的前面:

SQL

EXPLAIN SELECT * FROM users WHERE email = 'someuser@example.com';

你会得到一份详细的“作案手法说明书”:



+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | users | NULL | ALL | NULL | NULL | NULL | NULL | 1589432 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+

审讯报告解读:

  • table: users “作案”的地点,是在users这张表。
  • type: ALL 看到这个词,你的警报就应该拉到最响! ALL,就是MySQL在向你“招供”:“报告长官,我,执行了一次‘全表扫描’!”
  • rows: 1589432 这是它预估的,“为了完成这次任务,我需要翻看的书本数量”。 证据确凿,罪大恶极!

第四步:执行“正义”——用“索引”为慢查询“搭桥修路”

现在,我们要对这个“案件”,进行“技术改造”,彻底杜绝此类“蠢事”的再次发生。 我们的改造工具,就是“索引 (Index)”。

索引,就是我们为图书馆的某一类“找书方式”(比如,按“作者”查找),专门制作的一本“索引目录”(卡片目录)。 这本目录,本身是按作者姓名的拼音排好序的。当读者再来找“张三”的书时,图书管理员不再需要去翻遍全馆。他只需要,在这本小小的“作者目录”里,用“二分法”快速定位到“张三”那一页,上面清晰地记录着,张三的所有书,分别在哪几个“书架号”上。

然后,他直奔那几个书架,取书即可。整个过程,从几小时,缩短到了几秒钟。

我们现在,就为users表的email字段,创建一本这样的“索引目录”。

SQL

CREATE INDEX idx_email ON users(email);

idx_email是我们给这本“目录”起的名字。users(email)则指定了,这本目录,是针对users表的email字段来建立的。

“改造”后的成果验收

索引创建完成后,我们再来“审讯”一次我们那条SQL:

SQL

EXPLAIN SELECT * FROM users WHERE email = 'someuser@example.com';

这一次,你得到的“口供”,将会焕然一新:

+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key       | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | users | NULL       | ref  | idx_email     | idx_email | 515     | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+--

看看发生了什么翻天覆地的变化:

  • type: ref 不再是ALLref代表,MySQL是通过“索引引用”的方式,高效地进行了查找。
  • key: idx_email 它明确地告诉你,它使用了我们刚刚创建的那本叫idx_email的“索引目录”。
  • rows: 1 它预估,这次,它只需要检查1行数据,就能完成任务!

从检查158万行,到检查1行。性能的提升,何止百倍、千倍!

你,已是“数据库性能医生”

现在,你已经拥有了两项作为“数据库性能医生”的核心技能: 使用“慢查询日志”这份“病理报告”,精准地找到最消耗你应用资源的“性能病灶”。 然后,使用“索引”这把“微创手术刀”,为你的数据表,建立起高效的“信息高速公路”。

你不再是那个只能通过“重启大法”,来“祈祷”性能恢复的“门外汉”。你,已经是一个能用数据和科学,精准地为你的应用“心脏”,搭桥、疏通血管的“外科专家”。

知识库

MySQL与PostgreSQL安全加固指南:10个必做的生产环境安全配置

2025-8-27 10:24:16

知识库

宝塔面板备份教程:3款插件/工具实现网站自动异地备份

2025-8-28 9:25:08

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧