0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

MySQL数据库慢查询的排查思路和最佳实践

马哥Linux运维 来源:马哥Linux运维 2026-04-24 14:40 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

背景与问题

数据库慢查询是导致应用响应缓慢最常见的原因之一。当业务人员反馈“页面加载慢”、“查询超时”、“系统卡顿”时,很多运维人员的第一反应是让开发人员“加个索引”。但加索引只是优化查询的众多手段之一,盲目加索引不仅可能无效,还可能适得其反。

真正的慢查询优化需要系统的方法:首先确认慢查询的事实,然后分析查询的执行计划,理解数据库的查询优化器决策,找出真正的瓶颈所在,最后选择最合适的优化手段。优化手段包括但不限于:创建合适的索引、重写 SQL 语句、调整数据库配置、优化表结构、分表分库、引入缓存等。

本文以 MySQL 为例,详细讲解慢查询的排查思路、分析方法、优化手段和最佳实践。这些方法论同样适用于 PostgreSQL、Oracle 等主流数据库,只是具体命令和语法有所不同。

1 慢查询的发现与确认

1.1 开启慢查询日志

MySQL 的慢查询日志是排查慢查询的基础工具。默认情况下,慢查询日志是关闭的。需要通过配置启用。

查看当前慢查询配置:

-- 查看慢查询相关变量
SHOWVARIABLESLIKE'slow_query%';
SHOWVARIABLESLIKE'long_query_time';
SHOWVARIABLESLIKE'log_output';

-- 查看是否启用了慢查询日志
SHOWVARIABLESLIKE'slow_query_log';

-- 查看慢查询日志文件路径
SHOWVARIABLESLIKE'slow_query_log_file';

配置慢查询日志:

-- 开启慢查询日志
SETGLOBALslow_query_log ='ON';

-- 设置慢查询阈值为 1 秒(可以是浮点数)
SETGLOBALlong_query_time =1;

-- 设置日志输出格式(TABLE 或 FILE)
SETGLOBALlog_output ='FILE';

-- 设置慢查询日志文件路径
SETGLOBALslow_query_log_file ='/var/log/mysql/slow.log';

上述配置重启后会丢失,需要写入配置文件永久生效。编辑 MySQL 配置文件(/etc/mysql/my.cnf或/etc/my.cnf):

[mysqld]
# 开启慢查询日志
slow_query_log = 1

# 慢查询日志文件路径
slow_query_log_file = /var/log/mysql/slow.log

# 慢查询阈值(秒)
long_query_time = 1

# 记录没有使用索引的查询
log_queries_not_using_indexes = 1

# 将慢查询记录到表(mysql.general_log)
log_output = FILE

重启 MySQL 服务使配置生效:

systemctl restart mysql
systemctl restart mysqld

1.2 慢查询日志格式解读

MySQL 慢查询日志记录了每次慢查询的详细信息:

# Time: 2024-01-15T1045.123456Z
# User@Host: app_user[app_user] @ localhost []
# Query_time: 5.234567 Lock_time: 0.001234 Rows_sent: 100 Rows_examined: 50000
SET timestamp=1705315845;
SELECT * FROM orders WHERE user_id = 12345 AND status ='paid'ORDER BY created_at DESC LIMIT 20;

关键字段说明:Time是查询执行的时间;User@Host是执行查询的用户和主机;Query_time是查询实际执行时间(秒),这是最核心的指标;Lock_time是等待锁的时间;Rows_sent是返回给客户端的行数;Rows_examined是扫描的行数,这个数字与Rows_sent的比值反映了查询效率。

Rows_examined大而Rows_sent小,说明查询在扫描大量数据后才找到符合条件的记录,这是需要优化的典型特征。高效的查询应该让Rows_examined尽可能接近Rows_sent。

1.3 使用 mysqldumpslow 分析慢查询日志

直接阅读原始慢查询日志非常困难,mysqldumpslow工具可以对日志进行分析汇总:

# 安装 MySQL 后即可使用
mysqldumpslow -t 10 /var/log/mysql/slow.log

# 参数说明:
# -t N   显示前 N 条最慢的查询
# -s C   按平均查询时间排序(c=计数,t=时间,l=锁时间,r=返回行数)
# -s S   按总查询时间排序
# -s R   按平均扫描行数排序
# -a    不聚合相同的查询
# -g PAT  只显示匹配 pattern 的查询

# 示例:显示最慢的 10 条查询
mysqldumpslow -t 10 -s t /var/log/mysql/slow.log

# 显示扫描行数最多的查询
mysqldumpslow -t 10 -s r /var/log/mysql/slow.log

# 显示查询次数最多的查询
mysqldumpslow -t 10 -s c /var/log/mysql/slow.log

# 过滤特定表的查询
mysqldumpslow -t 10 -g'orders'/var/log/mysql/slow.log

# 使用正则过滤
mysqldumpslow -t 10 -a -g'SELECT.*FROM.*WHERE'/var/log/mysql/slow.log

1.4 使用 pt-query-digest 进行深度分析

pt-query-digest是 Percona Toolkit 中的专业分析工具,比mysqldumpslow功能更强大:

# 安装 Percona Toolkit
# Ubuntu/Debian
apt-get install percona-toolkit

# RHEL/CentOS
yum install percona-toolkit

# 基本用法
pt-query-digest /var/log/mysql/slow.log

# 输出到文件
pt-query-digest /var/log/mysql/slow.log > /tmp/query_analysis.txt

# 只分析最近 24 小时的慢查询(需要日志中有时间戳)
pt-query-digest --since'24h'/var/log/mysql/slow.log

# 分析特定时间的查询
pt-query-digest --since'2024-01-15 1000'--until'2024-01-15 1200'/var/log/mysql/slow.log

# 输出查询响应时间分布
pt-query-digest --typegenlog /var/log/mysql/slow.log

pt-query-digest的输出包括:查询执行时间分布直方图;按响应时间排序的查询列表;每种查询的出现次数、平均执行时间、扫描行数统计;查询执行计划摘要;可能导致问题的查询特征标记。

2 使用 EXPLAIN 分析查询执行计划

2.1 EXPLAIN 基本用法

EXPLAIN是分析 SQL 查询执行计划的核心命令,它告诉 MySQL 优化器将如何执行查询,是排查慢查询最重要的工具。

-- 基本格式
EXPLAINSELECT*FROMordersWHEREuser_id =12345;

-- 更详细的输出(包括扩展信息)
EXPLAINFORMAT=JSONSELECT*FROMordersWHEREuser_id =12345;

-- 查看 UPDATE、DELETE、INSERT 的执行计划
EXPLAINUPDATEordersSETstatus='shipped'WHEREorder_id =100;
EXPLAINDELETEFROMordersWHEREstatus='cancelled';

2.2 EXPLAIN 输出字段详解

EXPLAIN输出的每一列都包含重要的优化信息:

EXPLAINSELECTo.*, u.nameFROMorders o
JOINusersuONo.user_id = u.id
WHEREo.user_id =12345ANDo.status ='paid'
ORDERBYo.created_atDESCLIMIT20;

输出示例:

+----+-------------+-------+------+-------------+------+--------+-------+---------------------+
| id | select_type| table |type| key     | rows | filtered| Extra |
+----+-------------+-------+------+-------------+------+--------+-------+
| 1 | SIMPLE   | o   | ref | idx_user  |  50 | 100.00| Using |
| 1 | SIMPLE   | u   | const| PRIMARY   |  1 | 100.00|    |
+----+-------------+-------+------+-------------+------+--------+-------+

id是查询中 SELECT 的序号,复合查询中 id 越大优先级越高。select_type是查询类型:SIMPLE 是简单查询(不含子查询和 UNION);PRIMARY 是外层查询;SUBQUERY 是子查询;DERIVED 是派生表(FROM 中的子查询);UNION 是 UNION 操作的查询。table是涉及的表名。type是访问类型,表示 MySQL 决定如何查找表中的行,这是最关键的字段之一。possible_keys是可供选择的索引列表。key是实际选择的索引。key_len是使用的索引长度。ref是与索引比较的列。rows是预计需要扫描的行数。filtered是按条件过滤后剩余的百分比。Extra是额外信息,包含优化提示。

2.3 type 字段详解

type字段反映了查询的效率,从最好到最差依次是:system、const、eq_ref、ref、ref_or_null、index_merge、unique_subquery、index_subquery、range、index、ALL。

const是最优的类型,表示只匹配一行,通常是通过主键或唯一索引查找。eq_ref表示通过主键或唯一索引关联查询,每个索引值只对应一行记录。ref表示通过普通索引查找,返回所有匹配索引值的行。range表示使用索引范围查询(>、<、BETWEEN、IN 等)。index 表示全索引扫描,虽然比全表扫描好但仍然很慢。ALL 是最差的全表扫描,意味着没有使用任何索引。

如果查询的 type 是 ALL,说明没有使用索引,这是需要重点优化的对象。

-- 查看 type 为 ALL 的查询(需要优化的)
EXPLAINSELECT*FROMordersWHEREcreated_at >'2024-01-01';

-- 创建索引后,type 变为 range
CREATEINDEXidx_orders_created_atONorders(created_at);
EXPLAINSELECT*FROMordersWHEREcreated_at >'2024-01-01';

2.4 Extra 字段详解

Extra字段包含 MySQL 解析查询的额外信息,常见的值及其含义:

Using filesort表示 MySQL 无法利用索引完成排序,需要额外的排序操作。这是需要优化的信号。Using temporary表示查询需要创建临时表来存储结果,通常发生在 ORDER BY 和 GROUP BY 操作中。Using index表示使用了覆盖索引,查询只需要索引就能完成,不需要回表。Using index condition表示使用了索引下推优化。Using where表示在存储引擎层使用 WHERE 条件过滤。Using join buffer表示使用了连接缓存。

-- 出现 Using filesort,需要优化排序
EXPLAINSELECT*FROMordersWHEREuser_id =123ORDERBYcreated_atDESC;

-- 优化方案:利用索引消除 filesort
CREATEINDEXidx_orders_user_createdONorders(user_id, created_atDESC);

2.5 使用 EXPLAIN ANALYZE(MySQL 8.0+)

MySQL 8.0 引入了EXPLAIN ANALYZE,它不仅显示执行计划,还实际执行查询并报告真实的统计数据:

EXPLAINANALYZE
SELECTo.*, u.nameFROMorders o
JOINusersuONo.user_id = u.id
WHEREo.user_id =12345ANDo.status ='paid'
ORDERBYo.created_atDESCLIMIT20;

输出包括预估的 rows 和实际执行的 rows,以及实际执行时间。通过对比预估和实际,可以发现统计信息过时或执行计划错误的问题。

3 索引的创建与优化

3.1 索引原理概述

理解索引的原理是正确使用索引的前提。MySQL 中最常用的是 B+Tree 索引,它将数据按照 B+Tree 数据结构组织,每个叶子节点包含所有数据(聚簇索引)或数据指针(非聚簇索引)。

B+Tree 索引的特点:所有叶子节点在同一层级,树的高度低,查询效率稳定;叶子节点之间通过链表连接,支持范围查询;每个节点可以存储多个键值,宽度大,层级浅。

索引的优势:大幅减少查询需要扫描的数据量;避免排序和临时表生成;支持索引覆盖扫描,直接返回结果。

索引的代价:占用额外的磁盘空间;写入操作(INSERT、UPDATE、DELETE)需要维护索引,降低写入性能;创建索引需要加锁,可能阻塞其他操作。

3.2 创建索引的原则

创建索引需要遵循以下原则:

选择区分度高的列创建索引。区分度指列中不重复值的比例,比例越高,索引效率越好。使用SELECT COUNT(DISTINCT column) / COUNT(*) FROM table计算区分度。

-- 查看列的区分度
SELECTCOUNT(DISTINCTstatus) /COUNT(*)FROMorders;
SELECTCOUNT(DISTINCTuser_id) /COUNT(*)FROMorders;

-- status 区分度低(只有几种状态),user_id 区分度高
-- 应该为 user_id 创建索引,而不是 status

复合索引遵循最左前缀原则。复合索引INDEX idx(a, b, c)可以支持WHERE a = ?、WHERE a = ? AND b = ?、WHERE a = ? AND b = ? AND c = ?的查询,但不支持WHERE b = ?或WHERE c = ?的查询。

-- 创建复合索引
CREATEINDEXidx_orders_user_statusONorders(user_id,status, created_at);

-- 这些查询可以使用该索引
SELECT*FROMordersWHEREuser_id =123;
SELECT*FROMordersWHEREuser_id =123ANDstatus='paid';
SELECT*FROMordersWHEREuser_id =123ANDstatus='paid'ANDcreated_at >'2024-01-01';

-- 这个查询无法使用该索引(最左前缀不满足)
SELECT*FROMordersWHEREstatus='paid';

为主键和唯一约束创建唯一索引。唯一索引保证列值的唯一性,同时提供快速的唯一性查找。

3.3 索引创建实战

根据慢查询日志和 EXPLAIN 分析结果,为慢查询创建合适的索引:

-- 分析慢查询
-- SELECT * FROM orders WHERE user_id = 12345 AND status = 'paid' ORDER BY created_at DESC LIMIT 20;

-- 根据 WHERE 条件和 ORDER BY 创建复合索引
-- 1. 区分度高的列靠前:user_id > status
-- 2. ORDER BY 的列需要与 WHERE 条件一起创建索引
-- 3. 需要倒序排序,如果是 MySQL 8.0+ 可以创建倒序索引

CREATEINDEXidx_orders_user_status_createdONorders(user_id,status, created_atDESC);

-- 如果是 MySQL 5.7,需要创建两个索引
CREATEINDEXidx_orders_user_statusONorders(user_id,status);
CREATEINDEXidx_orders_user_createdONorders(user_id, created_atDESC);

3.4 索引使用注意事项

避免在索引列上使用函数或进行计算,这会导致索引失效:

-- 索引失效
SELECT*FROMordersWHEREYEAR(created_at) =2024;
SELECT*FROMordersWHEREcreated_at +INTERVAL1DAY>NOW();

-- 正确的做法
SELECT*FROMordersWHEREcreated_at >='2024-01-01'ANDcreated_at < '2025-01-01';

避免使用 LIKE 开头是通配符的查询:

-- 索引失效
SELECT*FROMordersWHEREorder_noLIKE'%123%';
SELECT*FROMordersWHEREorder_noLIKE'%123';

-- 可以使用索引
SELECT*FROMordersWHEREorder_noLIKE'ABC123%';

数据类型要匹配:

-- 如果 user_id 是 INT 类型
-- 错误:字符串与数字比较
SELECT*FROMordersWHEREuser_id ='12345';

-- 正确:使用相同类型
SELECT*FROMordersWHEREuser_id =12345;

3.5 查看索引使用情况

创建索引后,需要确认索引是否被使用:

-- 查看表的索引
SHOWINDEXFROMorders;

-- 使用 EXPLAIN 检查是否使用索引
EXPLAINSELECT*FROMordersWHEREuser_id =12345;

-- 查看索引的基数(Cardinality,反映区分度)
SHOWTABLESTATUSLIKE'orders';

-- MySQL 8.0+ 使用 INDEX_STATISTICS
SELECT*FROMmysql.index_statisticsWHEREtable_name ='orders';

4 SQL 语句优化

4.1 常见低效 SQL 模式

避免 SELECT *,只查询需要的列:

-- 低效:返回所有列
SELECT*FROMordersWHEREorder_id =12345;

-- 高效:只查询需要的列
SELECTorder_id, user_id,status, total_amount, created_at
FROMordersWHEREorder_id =12345;

使用 LIMIT 限制返回行数:

-- 低效:没有 LIMIT,可能返回大量数据
SELECT*FROMordersWHEREuser_id =12345ORDERBYcreated_atDESC;

-- 高效:使用 LIMIT
SELECT*FROMordersWHEREuser_id =12345ORDERBYcreated_atDESCLIMIT20;

分解大查询,减少单次查询的扫描范围:

-- 低效:一个复杂查询处理大量数据
SELECTo.*, u.*, p.*
FROMorders o
JOINusersuONo.user_id = u.id
JOINproducts pONo.product_id = p.id
WHEREo.created_at >'2024-01-01';

-- 高效:分步处理,先筛选出符合条件的订单,再关联其他表
SELECT*FROMordersWHEREcreated_at >'2024-01-01'LIMIT1000;
-- 然后用 IN 子句关联用户和产品
SELECTu.*FROMusersWHEREidIN(SELECTDISTINCTuser_idFROMordersWHEREcreated_at >'2024-01-01');

4.2 优化 ORDER BY 和 GROUP BY

ORDER BY 使用索引可以避免 filesort:

-- 创建复合索引使 ORDER BY 使用索引
CREATEINDEXidx_orders_user_status_createdONorders(user_id,status, created_atDESC);

-- 这个查询可以使用索引排序
SELECT*FROMorders
WHEREuser_id =123
ORDERBYstatus, created_atDESC
LIMIT20;

GROUP BY 同样可以利用索引:

-- 如果查询经常按时间分组统计,创建按时间的索引
CREATEINDEXidx_orders_created_atONorders(created_at);

-- 分组查询
SELECTDATE(created_at),COUNT(*),SUM(total_amount)
FROMorders
GROUPBYDATE(created_at);

4.3 优化 JOIN 操作

确保 JOIN 条件有索引:

-- orders.user_id 和 users.id 都应该有索引
CREATEINDEXidx_orders_user_idONorders(user_id);
CREATEINDEXidx_users_idONusers(id);

-- 使用 EXPLAIN 检查 JOIN 类型
EXPLAINSELECTo.*, u.name
FROMorders o
JOINusersuONo.user_id = u.id
WHEREo.user_id =12345;

小表驱动大表(让小表先过滤,减少被驱动表的扫描次数):

-- orders 表有 1000 万条记录,users 表有 10 万条记录
-- 正确:用小表驱动大表
SELECTo.*, u.name
FROMusersu
JOINorders oONo.user_id = u.id
WHEREu.status ='vip';

-- 可能效率更低
SELECTo.*, u.name
FROMorders o
JOINusersuONo.user_id = u.id
WHEREu.status ='vip';

4.4 使用 EXISTS 替代 IN

-- 子查询表很大的情况下,IN 的效率可能很低
SELECT*FROMorders
WHEREuser_idIN(SELECTidFROMusersWHEREstatus='vip');

-- 使用 EXISTS 通常更高效
SELECT*FROMorders o
WHEREEXISTS(SELECT1FROMusersuWHEREu.id = o.user_idANDu.status ='vip');

5 数据库配置优化

5.1 关键配置参数

MySQL 的配置文件对性能有重大影响。关键参数包括:

[mysqld]
# InnoDB 缓冲池大小,通常设置为可用内存的 70-80%
innodb_buffer_pool_size = 12G

# 日志文件大小
innodb_log_file_size = 1G

# 刷新日志的策略(影响写入性能和数据安全)
innodb_flush_log_at_trx_commit = 1

# 最大连接数
max_connections = 500

# 查询缓存(MySQL 8.0 已移除)
query_cache_size = 0
query_cache_type = 0

# 临时表和内存表大小
tmp_table_size = 256M
max_heap_table_size = 256M

# 慢查询日志
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1

# 记录未使用索引的查询
log_queries_not_using_indexes = 1

5.2 InnoDB 缓冲池优化

InnoDB 缓冲池是 InnoDB 最重要的性能参数,它缓存表数据和索引。缓冲池越大,可以缓存的数据越多,磁盘 I/O 越少。

-- 查看缓冲池使用情况
SHOWSTATUSLIKE'Innodb_buffer_pool%';

-- 查看缓冲池大小配置
SHOWVARIABLESLIKE'innodb_buffer_pool_size';

-- 动态调整缓冲池大小(MySQL 5.7+)
SETGLOBALinnodb_buffer_pool_size =12873741824; -- 12GB

-- 设置缓冲池实例数量(建议 CPU 核心数)
innodb_buffer_pool_instances = 8

-- 预热缓冲池(数据库重启后自动加载热数据到内存)
innodb_buffer_pool_load_at_startup = 1

5.3 连接数管理

连接数过多会导致资源耗尽,连接数过少会限制并发能力。

-- 查看当前连接数
SHOWSTATUSLIKE'Threads_connected';
SHOWSTATUSLIKE'Max_used_connections';

-- 查看最大连接数
SHOWVARIABLESLIKE'max_connections';

-- 调整最大连接数
SETGLOBALmax_connections =1000;

-- 查看连接来源
SHOWPROCESSLIST;
SHOWFULLPROCESSLIST;

-- 杀掉长时间空闲的连接
SELECTCONCAT('KILL ',id,';')
FROMinformation_schema.processlist
WHERECommand ='Sleep'ANDTime>3600;

6 慢查询优化实战案例

6.1 案例一:分页查询优化

后台管理系统中常见的需求是分页查询订单列表:

-- 低效的深分页查询(页面越大越慢)
SELECT*FROMordersORDERBYorder_idDESCLIMIT1000000,20;

-- 问题分析:
-- 1. LIMIT offset 很大时,MySQL 需要扫描 offset + limit 条记录
-- 2. rows_examined 会非常大
EXPLAINSELECT*FROMordersORDERBYorder_idDESCLIMIT1000000,20;

-- 优化方案一:使用游标分页(基于上一页最后一条记录的 ID)
SELECT*FROMorders
WHEREorder_id < 1234567
ORDERBY order_id DESC
LIMIT20;

-- 优化方案二:记录上次查询的位置
-- 前端记住 last_id = 1234567
-- 后端使用 WHERE order_id < last_id LIMIT 20

-- 优化方案三:如果必须使用 offset,使用覆盖索引
SELECT order_id FROM orders ORDERBY order_id DESCLIMIT1000000, 20;
-- 确认使用了索引而不是 filesort

6.2 案例二:统计查询优化

需要统计每天的订单数量和金额:

-- 低效:实时计算,每次查询都扫描全表
SELECTDATE(created_at)ASday,
   COUNT(*)ASorder_count,
   SUM(total_amount)AStotal_amount
FROMorders
WHEREcreated_at >='2024-01-01'
GROUPBYDATE(created_at);

-- 优化方案一:使用汇总表
CREATETABLEorders_daily_summary (
  stat_dateDATEPRIMARYKEY,
  order_countINTNOTNULLDEFAULT0,
  total_amountDECIMAL(15,2)NOTNULLDEFAULT0,
  updated_atTIMESTAMPDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP
);

-- 定期汇总(可以使用事件或 cron 任务)
INSERTINTOorders_daily_summary (stat_date, order_count, total_amount)
SELECTDATE(created_at),COUNT(*),SUM(total_amount)
FROMorders
WHEREDATE(created_at) ='2024-01-15'
GROUPBYDATE(created_at)
ONDUPLICATEKEYUPDATE
  order_count =VALUES(order_count),
  total_amount =VALUES(total_amount);

-- 查询汇总表
SELECT*FROMorders_daily_summaryWHEREstat_date >='2024-01-01';

6.3 案例三:模糊搜索优化

用户表需要支持按用户名模糊搜索:

-- 低效:LIKE 开头是通配符导致全表扫描
SELECT*FROMusersWHEREnameLIKE'%zhang%';

-- 优化方案一:使用全文索引(MySQL 5.6+)
ALTERTABLEusersADDFULLTEXTINDEXft_users_name(name);

SELECT*FROMusersWHEREMATCH(name) AGAINST('zhang');

-- 优化方案二:使用 Elasticsearch 等专业搜索引擎
-- 适用于模糊搜索需求复杂、对性能要求高的场景

-- 优化方案三:使用前缀索引(适用于前缀固定的场景)
CREATEINDEXidx_users_name_prefixONusers(name(10));

-- 如果 name 通常以姓开头
SELECT*FROMusersWHEREnameLIKE'zhang%'; -- 可以使用索引

7 监控与预防

7.1 持续监控慢查询

使用 pt-query-digest 定期分析慢查询日志:

#!/bin/bash
# 每天凌晨分析昨天的慢查询日志

DATE=$(date -d"yesterday"+%Y-%m-%d)
SLOW_LOG="/var/log/mysql/slow.log"
REPORT="/var/log/mysql/slow_query_report_${DATE}.txt"

pt-query-digest --since"$(date -d 'yesterday 0000' +%s)seconds"
        --until"$(date -d 'yesterday 2359' +%s)seconds"
        --report-format=query_report 
       $SLOW_LOG>$REPORT

# 如果有新的慢查询,发送告警
if[ -s"$REPORT"];then
  count=$(grep -c"Query"$REPORT||true)
 if["$count"-gt 10 ];then
   echo"Found$countslow queries in the report"| mail -s"Slow Query Alert"ops@example.com
 fi
fi

7.2 使用 Performance Schema

MySQL 5.6+ 提供了 Performance Schema,可以实时监控查询性能:

-- 启用相关的 instrument 和 consumer
UPDATEperformance_schema.setup_instruments
SETENABLED ='YES'
WHERENAMELIKE'statement/%';

UPDATEperformance_schema.setup_consumers
SETENABLED ='YES'
WHERENAMELIKE'events_statements%';

-- 查看当前最慢的查询
SELECT
  DIGEST,
  COUNT_STAR,
  SUM_TIMER_WAIT /1000000000000AStotal_time_sec,
  AVG_TIMER_WAIT /1000000000000ASavg_time_sec,
  SUM_ROWS_EXAMINED,
  SUM_ROWS_SENT,
 SUBSTR(DIGEST_TEXT,1,100)ASquery_sample
FROMperformance_schema.events_statements_summary_by_digest
ORDERBYSUM_TIMER_WAITDESC
LIMIT10;

7.3 建立慢查询治理流程

生产环境的慢查询治理应该成为日常工作的一部分:

日常监控:每天检查新增的慢查询

分析根因:使用 EXPLAIN 分析执行计划

制定优化方案:索引优化、SQL 改写、配置调整

上线验证:确认优化效果

归档记录:记录问题和解决方案,形成知识库

8 结论

数据库慢查询优化是一个系统性的工作,需要综合运用多种方法和工具。

首先,通过慢查询日志发现慢查询,使用 mysqldumpslow 或 pt-query-digest 进行分析;其次,使用 EXPLAIN 分析执行计划,找出性能瓶颈;然后,根据分析结果选择优化手段:创建合适的索引、重写 SQL 语句、调整数据库配置;最后,通过持续监控防止慢查询复发。

加索引只是优化手段之一,不是万能药。正确的优化思路是:先分析,再决策,最后实施。不加分析的盲目加索引可能适得其反。

运维工程师应该掌握的慢查询排查技能:开启和配置慢查询日志;使用 EXPLAIN 分析执行计划;创建和分析索引;识别常见的低效 SQL 模式;根据业务特点制定优化方案。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据库
    +关注

    关注

    7

    文章

    4083

    浏览量

    68538
  • Oracle
    +关注

    关注

    2

    文章

    308

    浏览量

    38948
  • MySQL
    +关注

    关注

    1

    文章

    931

    浏览量

    29748

原文标题:数据库慢查询怎么查?别再只会让开发加索引

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    深入优化DB2 数据库的五个最佳实践

    深入优化DB2 数据库的五个最佳实践   结构化查询语言(SQL)对于关系型DBMS是把双刃剑,利弊参半。因为从关系型数据库检索任何
    发表于 01-27 13:28 1125次阅读

    保护MySQL数据仓库的最佳实践

    数据仓库中最常见的数据库管理系统可能就是开源的MySQL数据库。以下5个小技巧重点介绍了一些保护MySQL
    发表于 09-27 14:10 0次下载

    TreeView Mysql查询数据库的详细资料合集免费下载

    本文档的主要内容详细介绍的是TreeView Mysql查询数据库的详细资料合集免费下载。
    发表于 12-12 08:00 0次下载
    TreeView <b class='flag-5'>Mysql</b><b class='flag-5'>查询</b><b class='flag-5'>数据库</b>的详细资料合集免费下载

    华为云数据库-RDS for MySQL数据库

    华为云数据库-RDS for MySQL数据库 华为云数据库作为华为云的一款数据库产品,它主要是以MyS
    的头像 发表于 10-27 11:06 2471次阅读

    MySQL数据库管理与应用

    MySQL数据库管理与应用 MySQL是一种广泛使用的关系型数据库管理系统,被认为是最流行和最常见的开源数据库之一。它可以被用于多种不同的应
    的头像 发表于 08-28 17:15 1941次阅读

    MySQL数据库基础知识

    的基础知识,包括其架构、数据类型、表操作、查询语句和数据导入导出等方面。 MySQL 数据库架构 MyS
    的头像 发表于 11-21 11:09 2001次阅读

    mysql数据库基础命令

    MySQL是一个流行的关系型数据库管理系统,经常用于存储、管理和操作数据。在本文中,我们将详细介绍MySQL的基础命令,并提供与每个命令相关的详细解释。 登录
    的头像 发表于 12-06 10:56 1547次阅读

    数据库数据恢复—MYSQL数据库ibdata1文件损坏的数据恢复案例

    mysql数据库故障: mysql数据库文件ibdata1、MYI、MYD损坏。 故障表现:1、数据库无法进行
    的头像 发表于 12-09 11:05 1390次阅读

    数据库数据恢复—Mysql数据库表记录丢失的数据恢复流程

    Mysql数据库故障: Mysql数据库表记录丢失。 Mysql数据库故障表现: 1、
    的头像 发表于 12-16 11:05 1348次阅读
    <b class='flag-5'>数据库</b><b class='flag-5'>数据</b>恢复—<b class='flag-5'>Mysql</b><b class='flag-5'>数据库</b>表记录丢失的<b class='flag-5'>数据</b>恢复流程

    MySQL数据库的安装

    MySQL数据库的安装 【一】各种数据库的端口 MySQL :3306 Redis :6379 MongoDB :27017 Django :8000 flask :5000 【二】
    的头像 发表于 01-14 11:25 1242次阅读
    <b class='flag-5'>MySQL</b><b class='flag-5'>数据库</b>的安装

    MySQL数据库是什么

    MySQL数据库是一种 开源的关系型数据库管理系统(RDBMS) ,由瑞典MySQL AB公司开发,后被Oracle公司收购。它通过结构化查询
    的头像 发表于 05-23 09:18 1445次阅读

    企业级MySQL数据库管理指南

    在当今数字化时代,MySQL作为全球最受欢迎的开源关系型数据库,承载着企业核心业务数据的存储与处理。作为数据库管理员(DBA),掌握MySQL
    的头像 发表于 07-09 09:50 888次阅读

    MySQL查询终极优化指南

    作为一名在生产环境摸爬滚打多年的运维工程师,我见过太多因为查询导致的线上故障。今天分享一套经过实战检验的MySQL查询分析与索引优化方法
    的头像 发表于 08-13 15:55 953次阅读

    MySQL数据库查询分析与优化实战

    在讨论MySQL查询之前,需要先明确一个关键前提:什么是查询? 不同业务场景下,
    的头像 发表于 04-02 09:38 171次阅读

    MySQL查询调优指南

    MySQL查询数据库性能问题的最常见原因。当一条SQL语句执行超过1秒时,就可能影响用户体验;超过10秒时,通常会收到用户投诉;而超过30秒的
    的头像 发表于 04-09 10:01 180次阅读