一个不可思议的MySQL慢查分析与解决
转自:http://fordba.com/optimize-an-amazing-mysql-slowlog.html?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
一、前言
开发需要定期的删除表里一定时间以前的数据,SQL如下
- mysql > delete from testtable WHERE biz_date <= '2017-08-21 00:00:00' AND status = 2 limit 500\G
前段时间在优化的时候,已经在相应的查询条件上加上了索引
- KEY `idx_bizdate_st` (`biz_date`,`status`)
但是实际执行的SQL依然非常慢,为什么呢,我们来一步步分析验证下
二、
表上的字段既然都有索引,那么按照之前的文章分析,是两个字段都可以走上索引的。如果有疑问,请参考文章 10分钟让你明白MySQL是如何利用索引的
既然能够利用索引,表的总大小也就是200M左右,那么为什么形成了慢查呢?
我们查看执行计划,去掉limit 后,发现他选择了走全表扫描。
- mysql > desc select * from testtable WHERE biz_date <= '2017-08-21 00:00:00';
- +----+-------------+-----------+------+----------------+------+---------+------+--------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-----------+------+----------------+------+---------+------+--------+-------------+
- | 1 | SIMPLE | testtable | ALL | idx_bizdate_st | NULL | NULL | NULL | 980626 | Using where |
- +----+-------------+-----------+------+----------------+------+---------+------+--------+-------------+
- 1 row in set (0.00 sec)
- -- 只查询biz_date
- -- 关键点:rows:980626;type:ALL
- mysql > desc select * from testtable WHERE biz_date <= '2017-08-21 00:00:00' and status = 2;
- +----+-------------+-----------+------+----------------+------+---------+------+--------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-----------+------+----------------+------+---------+------+--------+-------------+
- | 1 | SIMPLE | testtable | ALL | idx_bizdate_st | NULL | NULL | NULL | 980632 | Using where |
- +----+-------------+-----------+------+----------------+------+---------+------+--------+-------------+
- 1 row in set (0.00 sec)
- -- 查询biz_date + status
- -- 关键点:rows:980632;type:ALL
- mysql > desc select * from testtable WHERE biz_date <= '2017-08-21 00:00:00' and status = 2 limit 100;
- +----+-------------+-----------+-------+----------------+----------------+---------+------+--------+-----------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-----------+-------+----------------+----------------+---------+------+--------+-----------------------+
- | 1 | SIMPLE | testtable | range | idx_bizdate_st | idx_bizdate_st | 6 | NULL | 490319 | Using index condition |
- +----+-------------+-----------+-------+----------------+----------------+---------+------+--------+-----------------------+
- 1 row in set (0.00 sec)
- -- 查询biz_date + status+ limit
- -- 关键点:rows:490319;
- mysql > select count(*) from testtable WHERE biz_date <= '2017-08-21 00:00:00' and status = 2;
- +----------+
- | count(*) |
- +----------+
- | 0 |
- +----------+
- 1 row in set (0.34 sec)
- mysql > select count(*) from testtable WHERE biz_date <= '2017-08-21 00:00:00';
- +----------+
- | count(*) |
- +----------+
- | 970183 |
- +----------+
- 1 row in set (0.33 sec)
- mysql > select count(*) from testtable;
- +----------+
- | count(*) |
- +----------+
- | 991421 |
- +----------+
- 1 row in set (0.19 sec)
- mysql > select distinct biz_status from whwtestbuffer;
- +------------+
- | biz_status |
- +------------+
- | 1 |
- | 2 |
- | 4 |
- +------------+
通过以上查询,我们可以发现如下几点问题:
- 通过 biz_date 预估出来的行数 和 biz_date + status=2 预估出来的行数几乎一样,为98w。
- 实际查询表 biz_date + status=2
一条记录都没有
。 - 整表数据量达到了
99万
,MySQL发现通过索引扫描需要98w行
(预估)
因此,MySQL通过统计信息预估的时候,发现需要扫描的索引行数几乎占到了整个表,放弃了使用索引,选择了走全表扫描。
那是不是他的统计信息
有问题呢?我们重新收集了下表统计信息,发现执行计划的预估行数还是一样,猜测只能根据组合索引的第一个字段进行预估(待确定)
那我们试下直接强制让他走索引呢?
- mysql > select * from testtable WHERE biz_date <= '2017-08-21 00:00:00' and status = 2;
- Empty set (0.79 sec)
- mysql > select * from testtable force index(idx_bizdate_st) WHERE biz_date <= '2017-08-21 00:00:00' and status = 2;
- Empty set (0.16 sec)
我们发现,强制指定索引后,查询耗时和没有强制索引比较,的确执行速度快了很多,因为没有强制索引是全表扫描嘛!但是!依然非常慢!
那么还有什么办法去优化这个本来应该很快的查询呢?
大家应该都听说过要选择性好的字段放在组合索引的最前面?
是的,相对于status字段,biz_date 的选择性更加不错,那组合索引本身已经没有好调整了
那,能不能让他不要扫描索引的那么多范围呢?之前的索引模型中也说过,MySQL是通过索引去确定一个扫描范围,如果能够定位到尽可能小的范围,那是不是速度上会快很多呢?
并且业务逻辑上是定期删除一定日期之前的数据。所以逻辑上来说,每次删除都是只删除一天的数据,直接让SQL扫描一天的范围。那么我们就可以改写SQL啦!
- mysql > select * from testtable WHERE biz_date >= '2017-08-20 00:00:00' and biz_date <= '2017-08-21 00:00:00' and status = 2;
- Empty set (0.00 sec)
- mysql > desc select * from testtable WHERE biz_date >= '2017-08-20 00:00:00' and biz_date <= '2017-08-21 00:00:00' and status = 2;
- +----+-------------+------------------+-------+----------------+----------------+---------+------+------+-----------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+------------------+-------+----------------+----------------+---------+------+------+-----------------------+
- | 1 | SIMPLE | testtable | range | idx_bizdate_st | idx_bizdate_st | 6 | NULL | 789 | Using index condition |
- +----+-------------+------------------+-------+----------------+----------------+---------+------+------+-----------------------+
- 1 row in set (0.00 sec)
- -- rows降低了很多,乖乖的走了索引
- mysql > desc select * from testtable WHERE biz_date >= '2017-08-20 00:00:00' and biz_date <= '2017-08-21 00:00:00' ;
- +----+-------------+------------------+-------+----------------+----------------+---------+------+------+-----------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+------------------+-------+----------------+----------------+---------+------+------+-----------------------+
- | 1 | SIMPLE | testtable | range | idx_bizdate_st | idx_bizdate_st | 5 | NULL | 1318 | Using index condition |
- +----+-------------+------------------+-------+----------------+----------------+---------+------+------+-----------------------+
- 1 row in set (0.00 sec)
- -- 即使没有status,也是肯定走索引啦
三、
这个问题,我原本打算用hint,强制让他走索引,但是实际上强制走索引的执行时间并不能带来满意的效果。结合业务逻辑,来优化SQL,是最好的方式,也是终极法宝,一定要好好利用。不了解业务的DBA,不是一个好DBA... 继续去补业务知识去了。。
文章导航
本文目前有2条留言,欢迎发表评论!
- dikang123:
为什么不用pt-archive呢?
- 开发者头条:
感谢分享!已推荐到《开发者头条》:https://toutiao.io/posts/t0gxlp 欢迎点赞支持!
欢迎订阅《fordba》https://toutiao.io/subjects/266914
Write a Reply or Comment
一个不可思议的MySQL慢查分析与解决的更多相关文章
- MySQL死锁问题分析及解决方法实例详解(转)
出处:http://www.jb51.net/article/51508.htm MySQL死锁问题是很多程序员在项目开发中常遇到的问题,现就MySQL死锁及解决方法详解如下: 1.MySQL常用 ...
- <转>一个最不可思议的MySQL死锁分析
1 死锁问题背景 1 1.1 一个不可思议的死锁 1 1.1.1 初步分析 3 1.2 如何阅读死锁日志 3 2 死锁原因深入剖析 4 2.1 Delete操作的加锁逻辑 4 2.2 死锁预防策略 5 ...
- 一个最不可思议的MySQL死锁分析
1 死锁问题背景 1 1.1 一个不可思议的死锁 1 1.1.1 初步分析 3 1.2 如何阅读死锁日志 3 2 死锁原因深入剖析 4 2. ...
- 改进动态设置query cache导致额外锁开销的问题分析及解决方法-mysql 5.5 以上版本
改进动态设置query cache导致额外锁开销的问题分析及解决方法 关键字:dynamic switch for query cache, lock overhead for query cach ...
- mysql慢查日志分析工具 percona-toolkit
备忘自: http://blog.csdn.net/seteor/article/details/24017913 1. 工具简介 pt-query-digest是用于分析mysql慢查询的一个工具, ...
- Java中9种常见的CMS GC问题分析与解决
1. 写在前面 | 本文主要针对 Hotspot VM 中"CMS + ParNew"组合的一些使用场景进行总结.重点通过部分源码对根因进行分析以及对排查方法进行总结,排查过程会省 ...
- 海量数据分析更快、更稳、更准。GaussDB(for MySQL) HTAP只读分析特性详解
本文作者康祥,华为云数据库内核开发工程师,研究生阶段主要从事SPARQL查询优化相关工作.目前在华为公司参与华为云GaussDB(for MySQL) HTAP只读内核功能设计和研发. 1. 引言 H ...
- [转]一个用户SQL慢查询分析,原因及优化
来源:http://blog.rds.aliyun.com/2014/05/23/%E4%B8%80%E4%B8%AA%E7%94%A8%E6%88%B7sql%E6%85%A2%E6%9F%A5%E ...
- MySQL 5.7 Command Line Client输入密码后闪退和windows下mysql忘记root密码的解决办法
MySQL 5.7 Command Line Client输入密码后闪退的问题: 问题分析: 1.查看mysql command line client默认执行的一些参数.方法:开始->所有程序 ...
随机推荐
- Python面向对象篇(1)-类和对象
面向对象编程 1.编程范式 我们写代码的目的是什么?就是为了能够让计算机识别我们所写的代码并完成我们的需求,规范点说,就是通过编程,用特定的语法+数据结构+特殊算法来让计算机执行特定的功能,实现一 ...
- voip技术研究
voip:是一种通过ip现实电话通信的技术统称 sip:voip现在一般都采用sip协议 参考资料: android sip学习 问题: SipManager.newInstance(this)为nu ...
- linux centos下安装dokuwiki
首先先大致介绍一下wiki: DokuWiki是一个开源wiki引擎程序,运行于PHP环境下.Doku Wiki 程序小巧而功能强大.灵活,适合中小团队和个人网站知识库的管理. DokuWiki可以与 ...
- 微信小程序<web-view>嵌入网页后,小程序如何和网页交互传值?
最近开发一个项目由于小程序某些组件的限制,然后想到嵌入网页,但是遇到一个问题:网页端调取数据的时候需要 小程序传递多个参数值才能用,如何传值呢? 最初我想到是<web-view src=&quo ...
- 表单验证控件Verify.js
自己工作常用到表单录入验证,就顺手写了一个验证控件,刚开始写得很烂.多年后翻出来,又优化了一下,增加了一些功能.拿出来分享分享. 主要功能就是表单的录入验证. * 1.当录入框必填时,在控件后生成红色 ...
- JavaScript中的 true
经常看到有人写 如下代码,有时候也是凭经验猜想到底是什么意思,本着认真学习 JavaScript 的精神,专门写一篇去讨论这个问题. if(name){ //do something. }else{ ...
- <select>简易的二级联动
1.首先是表单页面: <tr> <td align="right"> <label class="Validform_label" ...
- java基础,集合,Arraylist,源码解析(基础)
ArrayList 是什么,定义? 这是动态的数组,它提供了动态的增加和减少元素,实现了List接口(List实现Collection,所以也实现Collection接口)灵活的设置数组的大小等好处 ...
- 【C#学习笔记之一】C#中的关键字
C#中的关键字 关键字是对编译器具有特殊意义的预定义保留标识符.它们不能在程序中用作标识符,除非它们有一个 @ 前缀.例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字. 下面是列出的 ...
- Codeforces Round #451 (Div. 2)-898A. Rounding 898B.Proper Nutrition 898C.Phone Numbers(大佬容器套容器) 898D.Alarm Clock(超时了,待补坑)(贪心的思想)
A. Rounding time limit per test 1 second memory limit per test 256 megabytes input standard input ou ...