记录一次mysql慢查询的优化过程

本文涉及的产品
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
RDS AI 助手,专业版
RDS Agent(兼容OpenClaw),2核4GB
简介: 优化mysql的慢查询语句,优化效果明显,记录一下。

前言

业务上线后经常报查询超时,数据库使用的是阿里云的RDS,mysql版本是5.6.16-log,有几条统计数据的查询语句执行很慢,有的甚至执行一次需要10多秒,简直无法忍受。
查看了超时时间,默认为0

show variables like '%MAX_STATEMENT_TIME%'
show variables like '%MAX_EXECUTION_TIME%'
# 网上说这个参数只针对5.7以上的版本生效
SET GLOBAL MAX_EXECUTION_TIME=30000;
# 对5.6版本生效
SET GLOBAL MAX_STATEMENT_TIME=30000;

然后在 select 后面 加上 /*+ max_statement_time(1000) */
设置后也没有效果,然后在阿里云上面手动修改对应的参数,也没用。

最后在阿里云上面看到几条优化建议,参考阿里云的建议,查询速度明显提升,这里也记录一下。

例1

表里面有810w+的数据
image.png

  1. 首先,就是建立正确的索引。
    多创建了两个联合索引,第一个是之前创建的,虽然能命中,但不是最佳。

    ALTER TABLE `user_event_record` ADD INDEX `idx_event_gmtcreate_userid` (`event`, `gmt_create`, `user_id`)    
    ALTER TABLE `user_event_record` ADD INDEX `idx_gmtcreate_event` (`gmt_create`, `event`)
    

    image.png

  2. 修改时间查询条件

  • 原sql
    SELECT 
      LEFT (f.gmt_create, 10 ) `date`,
      f.event,
      COUNT(1) total 
    FROM user_event_record f 
    WHERE
      date_format(f.gmt_create, '%y%m%d') >= date_format('2023-01-14', '%y%m%d') 
    GROUP BY `date`, f.event;
    
  • 修改后的sql

    SELECT 
      LEFT (f.gmt_create, 10) `date`,
      f.event,
      COUNT(1) total 
    FROM user_event_record f 
    WHERE
      f.gmt_create > DATE('2023-01-14') 
    GROUP BY `date`, f.event;
    

    查询速度对比

  • 原sql执行时间
    image.png

  • 修改后的sql执行时间
    image.png

直接提升了7-8倍,查询结果一致,效果提升还是很明显的。

例2

同一个表
原SQL

SELECT
        DATE_FORMAT(gmt_create, '%m-%d') AS `time`,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemFail' OR event = 'enterSystemSuc' THEN user_id END) AS total,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemSuc' THEN user_id END) AS success,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemFail' THEN user_id END) AS fail,
        COUNT(DISTINCT CASE WHEN app = '1.0.7' and (event = 'enterSystemFail' OR event = 'enterSystemSuc') THEN user_id END) AS oldCount,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemSuc' AND app = '1.0.7' THEN user_id END) AS oldSuccess,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemFail' AND app = '1.0.7' THEN user_id END) AS oldFail,
        COUNT(DISTINCT CASE WHEN app = '1.0.8' and (event = 'enterSystemFail' OR event = 'enterSystemSuc') THEN user_id END) AS newCount,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemSuc' AND app = '1.0.8' THEN user_id END) AS newSuccess,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemFail' AND app = '1.0.8' THEN user_id END) AS newFail
FROM user_event_record
WHERE DATE_FORMAT(gmt_create, '%Y-%m-%d') > DATE_SUB(CURDATE(), INTERVAL 10 DAY)
GROUP BY DATE_FORMAT(gmt_create, '%m-%d')

修改后的sql

SELECT
        DATE_FORMAT(gmt_create, '%m-%d') AS `time`,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemFail' OR event = 'enterSystemSuc' THEN user_id END) AS total,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemSuc' THEN user_id END) AS success,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemFail' THEN user_id END) AS fail,
        COUNT(DISTINCT CASE WHEN app = '1.0.7' and (event = 'enterSystemFail' OR event = 'enterSystemSuc') THEN user_id END) AS oldCount,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemSuc' AND app = '1.0.7' THEN user_id END) AS oldSuccess,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemFail' AND app = '1.0.7' THEN user_id END) AS oldFail,
        COUNT(DISTINCT CASE WHEN app = '1.0.8' and (event = 'enterSystemFail' OR event = 'enterSystemSuc') THEN user_id END) AS newCount,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemSuc' AND app = '1.0.8' THEN user_id END) AS newSuccess,
        COUNT(DISTINCT CASE WHEN event = 'enterSystemFail' AND app = '1.0.8' THEN user_id END) AS newFail
FROM user_event_record
WHERE gmt_create > DATE_SUB(CURDATE(), INTERVAL 9 DAY)
GROUP BY DATE_FORMAT(gmt_create, '%m-%d')

查询速度对比

  • 原sql执行时间
    image.png

  • 修改后的sql执行时间
    image.png

从28秒提升到3秒,提升了8-9倍

例3

表数据530w+
image.png

创建了联合索引

ALTER TABLE `file_record_info` ADD INDEX `idx_mark_gmtcreate_userid` (`mark`, `gmt_create`, `user_id`)

image.png

  • 原sql

    SELECT
      '2023-01-12' AS `date`,
      COUNT(DISTINCT CASE WHEN `mark` = '10000' THEN `user_id` ELSE NULL END) AS `upgradeFailCount`,
      COUNT(DISTINCT CASE WHEN `mark` = '10001' THEN `user_id` ELSE NULL END) AS `enterSysFailCount`,
      COUNT(DISTINCT CASE WHEN `mark` = '10008' THEN `user_id` ELSE NULL END) AS `dllLoadFailCount`,
      COUNT(DISTINCT CASE WHEN `mark` = '10009' THEN `user_id` ELSE NULL END) AS `enableDeviceFailCount`    
    FROM file_record_info 
    WHERE 
      mark IN ('10000', '10001', '10008', '10009' ) 
      AND DATE_FORMAT(gmt_create, '%Y-%m-%d') = '2023-01-12';
    
  • 修改后的sql

    SELECT
      '2023-01-12' AS `date`,
      COUNT(DISTINCT CASE WHEN mark = '10000' THEN user_id END) AS upgradeFailCount,
      COUNT(DISTINCT CASE WHEN mark = '10001' THEN user_id END) AS enterSysFailCount,
      COUNT(DISTINCT CASE WHEN mark = '10008' THEN user_id END) AS dllLoadFailCount,
      COUNT(DISTINCT CASE WHEN mark = '10009' THEN user_id END) AS enableDeviceFailCount
    FROM file_record_info WHERE mark IN ( '10000', '10001', '10008', '10009' )
    AND (`gmt_create` >= DATE('2023-01-12') AND `gmt_create` < DATE_ADD(DATE('2023-01-12'), INTERVAL 1 DAY));
    

    查询速度对比

  • 原sql执行时间
    image.png

  • 修改后的sql执行时间
    image.png

提升约30倍

例4

同样的例子

SELECT
/*+ MAX_EXECUTION_TIME(20000) */
    '2023-01-15' AS `date`,
    COUNT( DISTINCT CASE WHEN `event` = 'enterOBDSystemFail' OR `event` = 'enterOBDSystemSuc' THEN user_id END ) AS total,
    COUNT( DISTINCT CASE WHEN `event` = 'enterOBDSystemSuc' THEN user_id END ) AS success,
    COUNT( DISTINCT CASE WHEN `event` = 'enterOBDSystemFail' THEN user_id END ) AS fail 
FROM user_event_record 
WHERE
    `event` IN ( 'enterOBDSystemFail', 'enterOBDSystemSuc' ) 
    AND DATE_FORMAT( gmt_create, '%Y-%m-%d' ) = '2023-01-15';
  • 修改后的sql
    SELECT
      '2023-01-15' AS `date`,
      COUNT( DISTINCT CASE WHEN `event` = 'enterOBDSystemFail' OR `event` = 'enterOBDSystemSuc' THEN `user_id` ELSE NULL END ) AS `total`,
      COUNT( DISTINCT CASE WHEN `event` = 'enterOBDSystemSuc' THEN `user_id` ELSE NULL END ) AS `success`,
      COUNT( DISTINCT CASE WHEN `event` = 'enterOBDSystemFail' THEN `user_id` ELSE NULL END ) AS `fail` 
    FROM `user_event_record` 
    WHERE
      `event` IN ( 'enterOBDSystemFail', 'enterOBDSystemSuc' ) 
      AND `gmt_create` >= DATE( '2023-01-15' ) 
      AND `gmt_create` < DATE_ADD( DATE( '2023-01-15' ), INTERVAL 1 DAY );
    
    查询速度对比
  • 原sql执行时间
    image.png

  • 修改后的sql执行时间
    image.png

提升约40倍

例5

  • 原sql
    SELECT
      IFNULL( DATE_FORMAT( gmt_create, '%Y-%m-%d' ), '2022-12-01' ) `date`,
      COUNT( DISTINCT CASE WHEN mark = '10000' THEN user_id END ) AS upgradeFailCount,
      COUNT( DISTINCT CASE WHEN mark = '10001' THEN user_id END ) AS enterSysFailCount,
      COUNT( DISTINCT CASE WHEN mark = '10008' THEN user_id END ) AS dllLoadFailCount,
      COUNT( DISTINCT CASE WHEN mark = '10009' THEN user_id END ) AS enableDeviceFailCount 
    FROM file_record_info 
    WHERE
      mark IN ('10000', '10001', '10008', '10009') 
      AND DATE_FORMAT(gmt_create, '%Y-%m-%d')>='2022-12-01'
    GROUP BY `date`
    
  • 修改后的sql
    SELECT
      IFNULL( DATE_FORMAT( gmt_create, '%Y-%m-%d' ), '2022-12-01' ) `date`,
      COUNT( DISTINCT CASE WHEN mark = '10000' THEN user_id END ) AS upgradeFailCount,
      COUNT( DISTINCT CASE WHEN mark = '10001' THEN user_id END ) AS enterSysFailCount,
      COUNT( DISTINCT CASE WHEN mark = '10008' THEN user_id END ) AS dllLoadFailCount,
      COUNT( DISTINCT CASE WHEN mark = '10009' THEN user_id END ) AS enableDeviceFailCount 
    FROM file_record_info 
    WHERE
      mark IN ('10000', '10001', '10008', '10009') 
      AND gmt_create > DATE('2022-12-01') 
    GROUP BY `date`
    
    查询速度对比
  • 原sql执行时间
    image.png

  • 修改后的sql执行时间
    image.png

有所提升。

--------2023-05-13补充-------

例6:百万数据分页

  • 原sql
    SELECT  * FROM sys_oper_log WHERE  title like '%用户服务-上报用户事件%' LIMIT 1000000,10
    
  • 修改后的sql
    SELECT u.* FROM (SELECT oper_id FROM sys_oper_log WHERE title like '%用户服务-上报用户事件%' LIMIT 1000000,10) AS temp INNER JOIN sys_oper_log u ON u.oper_id = temp.oper_id
    
    查询速度对比
  • 原sql执行时间
    image.png

  • 修改后的sql执行时间
    image.png

这里是使用了覆盖索引,避免了回表操作,查询性能提升约60倍

总结

  1. 首先索引一定要根据业务来创建,不然没办法命中,可以通过EXPLAIN关键字来分析。
  2. 尽量避免DATE_FORMAT函数
  3. 优化sql,避免不必要的回表操作
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
11月前
|
存储 SQL 关系型数据库
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
|
12月前
|
SQL 缓存 关系型数据库
MySQL 慢查询是怎样优化的
本文深入解析了MySQL查询速度变慢的原因及优化策略,涵盖查询缓存、执行流程、SQL优化、执行计划分析(如EXPLAIN)、查询状态查看等内容,帮助开发者快速定位并解决慢查询问题。
464 0
|
SQL 监控 关系型数据库
MySQL慢查询攻略
本文详细介绍了MySQL慢查询优化的全流程,从定位性能瓶颈到具体优化策略,再到高级调优与预防监控。首先通过开启慢查询日志和分析工具(如pt-query-digest)找到问题SQL,接着从索引优化(如最左前缀原则、覆盖索引)、SQL语句重构(如避免全表扫描)及EXPLAIN执行计划解析等方面进行核心优化。随后深入参数调优和架构升级,如调整innodb_buffer_pool_size、实施分库分表等。最后,通过实时监控工具(如PMM、Prometheus+Grafana)建立长效机制,并以电商订单查询为例,展示优化前后性能大幅提升的实战效果。
1124 0
|
SQL 关系型数据库 MySQL
MySQL进阶突击系列(07) 她气鼓鼓递来一条SQL | 怎么看执行计划、SQL怎么优化?
在日常研发工作当中,系统性能优化,从大的方面来看主要涉及基础平台优化、业务系统性能优化、数据库优化。面对数据库优化,除了DBA在集群性能、服务器调优需要投入精力,我们研发需要负责业务SQL执行优化。当业务数据量达到一定规模后,SQL执行效率可能就会出现瓶颈,影响系统业务响应。掌握如何判断SQL执行慢、以及如何分析SQL执行计划、优化SQL的技能,在工作中解决SQL性能问题显得非常关键。
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
3479 10
|
10月前
|
缓存 关系型数据库 MySQL
降低MySQL高CPU使用率的优化策略。
通过上述方法不断地迭代改进,在实际操作中需要根据具体场景做出相对合理判断。每一步改进都需谨慎评估其变动可能导致其他方面问题,在做任何变动前建议先在测试环境验证其效果后再部署到生产环境中去。
393 6
|
11月前
|
存储 SQL 关系型数据库
MySQL 核心知识与索引优化全解析
本文系统梳理了 MySQL 的核心知识与索引优化策略。在基础概念部分,阐述了 char 与 varchar 在存储方式和性能上的差异,以及事务的 ACID 特性、并发事务问题及对应的隔离级别(MySQL 默认 REPEATABLE READ)。 索引基础部分,详解了 InnoDB 默认的 B+tree 索引结构(多路平衡树、叶子节点存数据、双向链表支持区间查询),区分了聚簇索引(数据与索引共存,唯一)和二级索引(数据与索引分离,多个),解释了回表查询的概念及优化方法,并分析了 B+tree 作为索引结构的优势(树高低、效率稳、支持区间查询)。 索引优化部分,列出了索引创建的六大原则
298 2
|
存储 SQL 关系型数据库
京东面试:mysql深度分页 严重影响性能?根本原因是什么?如何优化?
京东面试:mysql深度分页 严重影响性能?根本原因是什么?如何优化?
京东面试:mysql深度分页 严重影响性能?根本原因是什么?如何优化?
|
11月前
|
存储 SQL 关系型数据库
MySQL 动态分区管理:自动化与优化实践
本文介绍了如何利用 MySQL 的存储过程与事件调度器实现动态分区管理,自动化应对数据增长,提升查询性能与数据管理效率,并详细解析了分区创建、冲突避免及实际应用中的关键注意事项。
481 0
|
存储 关系型数据库 MySQL
MySQL细节优化:关闭大小写敏感功能的方法。
通过这种方法,你就可以成功关闭 MySQL 的大小写敏感功能,让你的数据库操作更加便捷。
1191 19

热门文章

最新文章

推荐镜像

更多