MySQL 8.0窗口函数优化SQL一例

简介: MySQL 8.0窗口函数优化SQL一例

1. 问题描述

最近在折腾把所有mysql slow query log写入到数据库中,再集中展示,向业务部门开放,也方便业务部门的同学自行查看并优化各自业务内的慢SQL。增加了定期生成报表的功能,统计最近1~2周内的慢查询数量变化情况,给业务方同学更直观的数据对比,了解最近这段时间的慢查询数量变化情况,是多了还是少了。于是有了下面这一坨SQL:

select hostname_max , db_max, sum(ts_cnt) as 1W
(select ifnull(sum(t1.ts_cnt),0) as ts_cnt from global_query_review_history t1 where 
t1.hostname_max=t2.hostname_max and t1.ts_min>= date_sub(now(), interval 14 day) and 
t1.ts_max<= date_sub(now(), interval 7 day)) AS 2W 
from global_query_review_history t2 where 
ts_min>= date_sub(now(), interval 7 day) 
group by hostname_max, db_max 
order by 1W desc limit 20;

当前 global_query_review_history 表约有2.5万条记录,这条SQL耗时 1.16秒,显然太慢了。下面是SQL执行计划:

*************************** 1. row ***************************

id: 1
select_type: PRIMARY
table: t2
partitions: NULL
type: ALL
possible_keys: ts_min
key: NULL
key_len: NULL
ref: NULL
rows: 25198
filtered: 41.09
Extra: Using where; Using temporary; Using filesort
2. row **
id: 2
select_type: DEPENDENT SUBQUERY
table: t1
partitions: NULL
type: ref
possible_keys: hostname_max,ts_min
key: hostname_max
key_len: 258
ref: func
rows: 20
filtered: 14.90
Extra: Using where

可以看到需要进行一次子查询(无法自动优化成JOIN)。

SQL执行后的status统计值:

+-----------------------+--------+
| Variable_name | Value |
+-----------------------+--------+
| Handler_read_first | 0 |
| Handler_read_key | 17328 |
| Handler_read_last | 0 |
| Handler_read_next | 809121 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 25380 |
+-----------------------+--------+

可以看到除了有全表扫描外,还要根据索引的多次逐行扫描(Handler_read_next = 809121,子查询引起的)。

2. SQL优化

上面的SQL主要瓶颈在于嵌套子查询,去掉子查询,即便是全表扫描也还是很快的。

[root@yejr.run]> select ...
...
20 rows in set (0.08 sec)

[root@yejr.run]> show status like 'handler%read%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 16910 |
| Handler_read_last | 0 |
| Handler_read_next | 0 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 25380 |
+-----------------------+-------+

SQL优化有困难自然先想到了松华老师,在得知我用的MySQL 8.0之后,他帮忙给改造成了基于窗口函数的写法:

select hostname_max , db_max,
sum( case when ts_min>= date_sub(now(), interval 7 day) then ts_cnt end ) as 1W,
ifnull(sum(case when ts_min>= date_sub(now(), interval 14 day)
and ts_max<= date_sub(now(), interval 7 day) then ts_cnt end ) over(partition by hostname_max),0) 2W
from global_query_review_history t2
where ts_min>= date_sub(now(), interval 14 day)
group by hostname_max, db_max
order by 1W desc limit 20;

再看下执行计划:

 1. row **
id: 1
select_type: SIMPLE
table: t2
partitions: NULL
type: ALL
possible_keys: ts_min
key: NULL
key_len: NULL
ref: NULL
rows: 25198
filtered: 44.88
Extra: Using where; Using temporary; Using filesort

新SQL比较取巧,只需要读取一次数据,利用窗口函数直接计算出需要的统计值。虽然有可用索引,但因为要扫描的数据量比较大,所以最后还是变成全表扫描。新SQL耗时和status统计值见下:

20 rows in set (0.08 sec)

[root@yejr.run]> show status like 'handler%read%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 24396 |
| Handler_read_last | 0 |
| Handler_read_next | 0 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 886 |
| Handler_read_rnd_next | 26703 |
+-----------------------+-------+

和之前那个SQL差距太大了,优化效果杠杠滴。

全文完。

Enjoy MySQL 8.0 :)

            </div>
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
Java Spring 容器
如何解决spring EL注解@Value获取值为null的问题
本文探讨了在使用Spring框架时,如何避免`@Value(&quot;${xxx.xxx}&quot;)`注解导致值为null的问题。通过具体示例分析了几种常见错误场景,包括类未交给Spring管理、字段被`static`或`final`修饰以及通过`new`而非依赖注入创建对象等,提出了相应的解决方案,并强调了理解框架原理的重要性。
836 5
|
SQL 算法 Java
数据库系列课程(08)-SpringBoot整合Sharding-Jdbc
数据库系列课程(08)-SpringBoot整合Sharding-Jdbc
654 0
|
NoSQL 算法 网络安全
一份完整的阿里云 Redis 开发规范,值得收藏!
一份完整的阿里云 Redis 开发规范,值得收藏!
2222 0
|
机器学习/深度学习 数据采集 PyTorch
使用PyTorch解决多分类问题:构建、训练和评估深度学习模型
使用PyTorch解决多分类问题:构建、训练和评估深度学习模型
使用PyTorch解决多分类问题:构建、训练和评估深度学习模型
|
人工智能 Rust 开发者
【MCP教程系列】使用Python在阿里云百炼创建基于UVX的MCP服务完整指南
本文介绍如何使用基于uvx工具链的Python项目,结合阿里云百炼平台实现小红书文案审核助手的MCP服务开发与部署。首先通过安装uv工具初始化项目并配置虚拟环境,编写server.py文件调用qwen-plus模型完成内容审核功能。随后将项目打包上传至PyPI,供全球开发者访问。接着在阿里云百炼平台上配置并部署该MCP服务,解决可能的依赖问题。最后,在智能体应用中引入此MCP服务进行测试验证,确保其正常运行。
3064 0
|
6月前
|
Prometheus 监控 Cloud Native
|
人工智能 程序员 开发者
新手指南: 微软Copilot国内能用吗?
微软Copilot 是由 GitHub 和 OpenAI 联合开发的一款 AI 结对编程工具,它就像一位经验丰富的程序员,在你编写代码时提供实时的代码建议和补全。Copilot 基于 OpenAI 的 Codex 模型,该模型经过了数十亿行公开代码的训练,能够理解多种编程语言和框架。
|
JavaScript
vue实现点击按钮将指定文字复制到系统剪切板并提示复制成功
vue实现点击按钮将指定文字复制到系统剪切板并提示复制成功
181 0
|
NoSQL 安全 容灾
阿里云DTS踩坑经验分享系列|Redis迁移、同步
阿里云数据传输服务DTS在帮助用户迁移Redis数据、同步数据时,在某些复杂场景下会出现报错,或者源库与目标库数据不一致的问题,给用户带来困扰。本文介绍了DTS Redis到Redis迁移、同步过程中的典型问题,以帮助用户更好地使用DTS。
1337 2
Java SpringBoot集成WebSocket根据登陆权限认证
Java SpringBoot集成WebSocket根据登陆权限认证