本文为MySQL数据库管理员和开发人员提供了一套全面的超时SQL定位和优化解决方案。通过合理运用这些方法和技巧,可以显著提升MySQL数据库的性能和稳定性,减少超时SQL语句的发生,确保数据库的高效运行。
最近某个Mysql数据库频繁告警,监控系统提醒有执行时间超长sql(基本上是执行时间超过1800秒)的语句。
之前查的问题时存在某个Mysql8.0的Bug,Kill掉某个sql语句后,该sql语句实际还在运行,只能重启解决。但是由于是生产数据库。重启十分流程麻烦。进行两次维护重启后还是频繁告警,我在怀疑系统开发的sql语句到底写的是什么东西(只管查,不管耗时)。必须要和他们battle一番。要不最后还是我来背锅~
最近一次执行时间超过30分钟的还是有70+条
超长执行sql占用大量的数据库资源,如 CPU、内存和 I/O 带宽,导致其他并发的查询和操作响应变慢,甚至可能导致数据库死机。
今天就来和大家一起分析一下查询超长执行时间sql语句的方法和解决预防防控措施
数据量过大:
复杂的查询逻辑:
比如,一个查询同时涉及 5 个表的连接,每个表又有复杂的筛选条件,并且还包含多层子查询来获取相关的关联数据。
缺少合适的索引:
例如,在一个经常根据用户 ID 进行查询的用户表中,如果没有为用户 ID 字段创建索引,每次查询都要遍历整个表。
服务器资源不足:
当服务器的内存不足时,可能会频繁进行磁盘交换,从而大大降低查询的执行速度。
网络延迟:
数据库配置不合理:
比如,缓冲池大小不足以容纳常用的数据,导致频繁的磁盘读取。
并发操作过多:
下图为网络段子
你问我外包算什么东西? 我现在告诉你: 1:你们正编不敢连表查询,我们外包10张表都敢连 2:你们正编不敢在循环里查询数据库,我们外包敢 3:你们正编写代码考虑扩展性,我们外包直接写死 总之一句话正编做得了的,我们外包要做,正编做不了的我们外包更要做,够不够清楚
正确的sql习惯可以在保证正确查询结果的前提下降低系统内存,IO的消耗,避免系统出现异常问题。
下面我将从可能导致数据库执行时间超长查询sql的情况来逐一分析并给出解决建议。
对于数据量较大的sql语句(百万行及以上),万万不可不可直接select *
,执行时间很长不说,也很难找到我们需要的关键信息
以下是一个可能对千万级数据量的订单表进行全表扫描查询的示例 SQL 语句:
SELECT * FROM orders;
在这个示例中,*
表示选择所有列,orders
是订单表的表名。由于没有指定任何条件,这将导致对整个订单表进行全表扫描。
另一个示例是:
SELECT order_id, customer_name, total_amount FROM orders;
这个语句选择了 order_id
、customer_name
和 total_amount
这几列,但同样没有添加任何筛选条件,仍然会进行全表扫描。
需要注意的是,在实际应用中,对如此大规模的数据量进行全表扫描通常是不推荐的,因为这可能会导致性能严重下降。应该尽量根据具体的业务需求添加合适的条件来缩小查询范围,例如:
SELECT * FROM orders WHERE order_date > '2024-08-01';
这个语句只会查询订单日期在 2024 年 8 月 1 日之后的订单,从而减少需要扫描的数据量,提高查询性能。
同样也可以限制查询结果数量
下面sql语句是从 orders
表中选取前 10 行的数据
SELECT * FROM orders BY order_id LIMIT limit 10;
对于复杂查询逻辑sql,我们尽量分开查询,避免一次执行,这样虽然可能看似方便,实际执行时间太长,导致查询效率很低
部分优化方法如下
使用合适的索引:
例如,如果经常根据 order_date
字段进行查询,可以创建索引:CREATE INDEX idx_order_date ON orders (order_date);
分解复杂查询:
比如,原本一个包含多个子查询和连接的复杂查询,可以拆分为先获取一部分数据,然后基于这部分数据再进行后续的查询。
避免不必要的计算和函数:
在WHERE
子句中尽量避免使用复杂的计算和函数,这可能会导致索引无法使用。
例如,不要写成 WHERE YEAR(order_date) = 2024
,而是直接写成 WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'
。
优化连接操作:
比如,在连接两个表时,使用具有索引的 id
字段进行关联。
减少数据量:
先使用 WHERE
子句筛选出符合条件的记录,再进行其他操作。
使用临时表:
调整数据库配置参数:
例如,有一个复杂的查询语句:
SELECT o.order_id, c.customer_name, SUM(o.order_amount) AS total_amount
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.order_date BETWEEN '2024-01-01' AND '2024-08-06'
GROUP BY o.order_id, c.customer_name;
优化后的可能如下:
-- 先创建一个临时表存储筛选后的订单数据
CREATE TEMPORARY TABLE temp_orders AS
SELECT * FROM orders WHERE order_date BETWEEN '2024-01-01' AND '2024-08-06';
-- 基于临时表进行连接和聚合操作
SELECT o.order_id, c.customer_name, SUM(o.order_amount) AS total_amount
FROM temp_orders o
JOIN customers c ON o.customer_id = c.customer_id
GROUP BY o.order_id, c.customer_name;
通过这样的优化,可以提高查询的性能和效率。
Mysql数据库常见配置文件名称如下
/etc/mysql/my.cnf
/etc/my.cnf
/etc/mysql/mysqld.cnf
/etc/mysql/conf.d/
(这里可能包含多个配置文件)/usr/local/mysql/etc/my.cnf
(如果 MySQL 是通过源码编译安装的话)
在配置文件中修改下面的信息,附内核配置文件优化修改脚本
调整缓冲池大小(InnoDB Buffer Pool):
innodb_buffer_pool_size = 总内存的 50% - 80%
较大的缓冲池可以减少磁盘 I/O,加快数据访问速度。
增加并发线程数(Thread Concurrency):
thread_concurrency = CPU 核心数 * 2
这有助于提高并发处理能力。
优化查询缓存(Query Cache):
query_cache_type = 0 # 除非您的应用有大量相同的查询,否则建议关闭
调整连接参数:
max_connections = 根据预期的并发连接数设置
优化 InnoDB 日志文件:
innodb_log_file_size = 256M # 根据写入量适当调整
innodb_log_buffer_size = 16M # 缓冲日志数据
调整排序缓冲区大小(Sort Buffer Size):
sort_buffer_size = 2M # 根据需要适当增大
调整读缓冲区大小(Read Buffer Size):
read_buffer_size = 2M # 适当调整
启用并行查询(Parallel Query):
innodb_parallel_read_threads = 4 # 根据硬件配置调整
Mysql配置参数优化脚本
下面脚本可以保存后执行,但是请确认你的配置文件路径,仅供参考
#!/bin/bash
# 备份原配置文件
# 这一步很重要,万一修改配置文件后出现问题,可以回滚到原始状态
cp /etc/my.cnf /etc/my.cnf.bak
# 打开配置文件进行编辑
vi /etc/my.cnf
# 添加或修改以下参数
# [mysqld] 部分表示以下的参数是针对 mysqld 服务的
echo "[mysqld]" >> /etc/my.cnf
# innodb_buffer_pool_size:InnoDB 缓冲池的大小,用于缓存表和索引数据。较大的值可以减少磁盘 I/O,提高性能。
# 这里设置为 512M,您可以根据服务器内存大小进行调整。
echo "innodb_buffer_pool_size = 512M" >> /etc/my.cnf
# thread_concurrency:设置并发线程数,根据服务器的 CPU 核心数进行适当调整,以提高并发处理能力。
echo "thread_concurrency = 8" >> /etc/my.cnf
# query_cache_type:查询缓存类型,设置为 0 表示关闭查询缓存。除非您的应用有大量重复的查询,否则关闭可能更好。
echo "query_cache_type = 0" >> /etc/my.cnf
# max_connections:设置最大并发连接数,根据预期的并发连接需求进行设置。
echo "max_connections = 200" >> /etc/my.cnf
# innodb_log_file_size:InnoDB 日志文件的大小,适当调整可以优化性能和恢复时间。
echo "innodb_log_file_size = 256M" >> /etc/my.cnf
# innodb_log_buffer_size:InnoDB 日志缓冲区的大小,缓冲日志数据以减少磁盘写入次数。
echo "innodb_log_buffer_size = 16M" >> /etc/my.cnf
# sort_buffer_size:排序缓冲区大小,用于排序操作,根据需要适当调整。
echo "sort_buffer_size = 2M" >> /etc/my.cnf
# read_buffer_size:读缓冲区大小,用于顺序读取数据,适当调整。
echo "read_buffer_size = 2M" >> /etc/my.cnf
# innodb_parallel_read_threads:InnoDB 并行读线程数,根据硬件配置调整,提高读取性能。
echo "innodb_parallel_read_threads = 4" >> /etc/my.cnf
下面我将介绍集中查询到底哪些sql语句在超时执行的语句,记录下来做针对性优化。
SHOW PROCESSLIST
命令SHOW PROCESSLIST;
这个命令会显示当前正在执行的所有连接和它们正在执行的 SQL 语句,以及执行的状态、时间等信息。您可以通过观察 Time
列来判断哪些语句执行时间较长。
首先,需要在 MySQL 的配置文件(通常是 my.cnf
或 my.ini
)中进行相关设置:
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 2 # 单位为秒,设置超过多长时间的查询被认为是慢查询
然后,重启 MySQL 服务使配置生效。之后,执行时间超过设定阈值的 SQL 语句会被记录到指定的慢查询日志文件中。
例如 pt-query-digest
工具,它可以对 MySQL 的查询日志(包括慢查询日志)进行分析,帮助您找出执行效率低下的 SQL 语句。
例如,如果您的慢查询日志文件名为 mysql-slow.log
,可以使用以下命令进行分析:
pt-query-digest mysql-slow.log
下图可以查询24h内执行时间超过AVG_TIMER_WAIT
的语句和相关执行用户
-- 从 performance_schema.events_statements_summary_by_digest 表中选择数据
SELECT *
FROM performance_schema.events_statements_summary_by_digest
-- 筛选条件:摘要文本不包含'performance_schema'
WHERE DIGEST_TEXT NOT LIKE '%performance_schema%'
-- 平均等待时间大于1小时
AND AVG_TIMER_WAIT > 3600000000000
-- 最后出现时间在过去 24 小时内
AND LAST_SEEN >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
-- 按照平均等待时间降序排序
ORDER BY AVG_TIMER_WAIT DESC\G
作为DBA,善于利用shell脚本和定时任务可以极大的减轻工作量,提高工作效率。
我们可以通过编写定时任务的方法每天执行时间较长的sql查询语句,获取其相关信息到txt文本中。
slow_query_check.sql
,内容如下:SELECT *
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST_TEXT NOT LIKE '%performance_schema%'
AND AVG_TIMER_WAIT >3600000000000
AND LAST_SEEN >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
ORDER BY AVG_TIMER_WAIT DESC\G
然后,使用 crontab -e
命令编辑定时任务。
在打开的文件中添加以下内容:
0 8 * * * 登录数据的命令,需要包含密码 < /home/mysql/zhangfakai/slow_query_check.sql > /home/mysql/zhangfakai/$(date +\%Y\%m\%d_slow_query_result.txt)
每天早上 8 点就会自动执行查询,并将结果输出到指定的 txt
文本中。
我们每天上班后可以先查询txt查看有无异常。
本文详细探讨了MySQL数据库中出现超时SQL语句的原因、定位方法、以及相应的优化和预防措施。通过深入分析,我们了解到MySQL数据库超时执行SQL的问题主要源于数据量过大、复杂的查询逻辑、缺少合适的索引、服务器资源不足、网络延迟、数据库配置不合理以及并发操作过多等多方面因素。
针对这些问题,本文提出了多项优化策略。在SQL查询优化方面,强调了避免大表全表扫描、优化复杂查询逻辑、使用合适的索引、分解复杂查询、避免不必要的计算和函数、优化连接操作、减少数据量以及使用临时表等技巧。同时,也介绍了如何通过调整数据库配置参数(如缓冲池大小、并发线程数、查询缓存、连接参数等)来优化数据库性能。
在超时SQL语句的定位方面,本文介绍了使用SHOW PROCESSLIST命令、开启慢查询日志、利用性能分析工具(如pt-query-digest)以及查询近期长时间执行的SQL语句等多种方法。这些方法有助于快速定位问题SQL语句,从而进行针对性的优化。
最后,本文还提出了编写超长SQL监控脚本的建议,通过定时任务自动检测长时间执行的SQL语句,并生成报告,帮助DBA及时发现问题并进行处理。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。