原标题:技术分享 | MySQL 优化:为什么 SQL 走索引还那么慢?

背景

2019-01-11 9:00-10:00 一个 MySQL 数据库把 CPU 打满了。

硬件配置:256G 内存,48 core

分析过程

接手这个问题时现场已经不在了,信息有限,所以我们先从监控系统中查看一下当时的状态。从 PMM 监控来看,这个 MySQL 实例每天上午九点 CPU 都会升高到 10%-20%,只有 1 月 2 号 和 1 月 11 号 CPU 达到 100%,也就是今天的故障。怀疑是业务在九点会有压力下发,排查方向是慢查询。

1. 按执行次数统计 slow log 发现次数最多的一条 sql:

mysqldumpslow -s c slow.log>/tmp/slow_report.txt

Count: 3276 Time=21.75s (71261s) Lock=0.00s (1s) Rows=0.9 (2785), xxx

SELECT T.TASK_ID,

T.xx,

T.xx,

...

FROM T_xx_TASK T

WHERE N=N

AND T.STATUS IN (N,N,N)

AND IFNULL(T.MAX_OPEN_TIMES,N) > IFNULL(T.OPEN_TIMES,N)

AND (T.CLOSE_DATE IS NULL OR T.CLOSE_DATE >= SUBDATE(NOW,INTERVAL 'S' MINUTE))

AND T.REL_DEVTYPE = N

AND T.REL_DEVID = N

AND T.TASK_DATE >= 'S'

AND T.TASK_DATE <= 'S'

ORDER BY TASK_ID DESC

LIMIT N,N

2. 在 slow log 中找到这条查询记录扫描行数:“Rows_examined: 1161559”,看起来是全表扫描,CPU 升高通常原因就是同时执行大量慢 sql,所以接下来分析这个 sql

3. 因为 T_ xxx_TASK 表在现场应急时清理过数据(从 110 万删至 4 万行),所以需要用备份恢复该表到故障前。恢复备份后,查看执行计划与执行时间:

explain SELECT T.TASK_ID,

T.xx,

...

FROM T_xxx_TASK T

WHERE 1=1

AND T.STATUS IN (1,2,3)

AND IFNULL(T.MAX_OPEN_TIMES,0) > IFNULL(T.OPEN_TIMES,0)

AND (T.CLOSE_DATE IS NULL OR T.CLOSE_DATE >= SUBDATE(NOW,INTERVAL '10' MINUTE))

AND T.REL_DEVTYPE = 1

AND T.REL_DEVID = 000000025xxx

AND T.TASK_DATE >= '2019-01-11'

AND T.TASK_DATE <= '2019-01-11'

ORDER BY TASK_ID DESC

LIMIT 0,20;

执行时间 10s+:

1 row in set (10.37 sec)

表索引信息:

show index fromT_xxx_TASK;

看到这里其实已经可以基本确定是这个 SQL 引起的了,因为执行一次就要 10s+,而且那个时间点会并发下发大量的这个 SQL。但是有一点陷阱藏在这里:

1. 执行计划中明明有使用到索引,为什么执行还是这么慢?

2. 执行计划中显示扫描行数为 644,为什么 slow log 中显示 100 多万行?

a. 我们先看执行计划,选择的索引 “INDX_ BIOM_ELOCK_ TASK3(TASK_ID)”。结合 sql 来看,因为有 "ORDER BY TASK_ ID DESC" 子句,排序通常很慢,如果使用了文件排序性能会更差,优化器选择这个索引避免了排序。

那为什么不选 possible_keys:INDX_ BIOM_ELOCK_ TASK 呢?原因也很简单,TASK_DATE 字段区分度太低了,走这个索引需要扫描的行数很大,而且还要进行额外的排序,优化器综合判断代价更大,所以就不选这个索引了。不过如果我们强制选择这个索引(用 force index 语法),会看到 SQL 执行速度更快少于 10s,那是因为优化器基于代价的原则并不等价于执行速度的快慢;

b. 再看执行计划中的 type:index,"index" 代表 “全索引扫描”,其实和全表扫描差不多,只是扫描的时候是按照索引次序进行而不是行,主要优点就是避免了排序,但是开销仍然非常大。

Extra:Using where 也意味着扫描完索引后还需要回表进行筛选。一般来说,得保证 type 至少达到 range 级别,最好能达到 ref。

在第 2 点中提到的“慢日志记录Rows_examined: 1161559,看起来是全表扫描”,这里更正为“全索引扫描”,扫描行数确实等于表的行数;

c. 关于执行计划中:“rows:644”,其实这个只是估算值,并不准确,我们分析慢 SQL 时判断准确的扫描行数应该以 slow log 中的 Rows_examined 为准。

4. 优化建议:添加组合索引 IDX_ REL_DEVID_ TASK_ID(REL_ DEVID,TASK_ID)

优化过程:

TASK_DATE 字段存在索引,但是选择度很低,优化器不会走这个索引,建议后续可以删除这个索引:

select count(*),count(distinct TASK_DATE) from T_BIOMA_ELOCK_TASK;

+------------+---------------------------+

| count(*) | count(distinct TASK_DATE) |

+------------+---------------------------+

+------------+---------------------------+

在这个 sql 中 REL_DEVID 字段从命名上看选择度较高,通过下面 sql 来检验确实如此:

select count(*),count(distinct REL_DEVID) from T_BIOMA_ELOCK_TASK;

+----------+---------------------------+

| count(*) | count(distinct REL_DEVID) |

+----------+---------------------------+

+----------+---------------------------+

由于有排序,所以得把 task_ id 也加入到新建的索引中,REL_DEVID,task_id 组合选择度 100%:

select count(*),count(distinct REL_DEVID,task_id) from T_BIOMA_ELOCK_TASK;

+----------+-----------------------------------+

| count(*) | count(distinct REL_DEVID,task_id) |

+----------+-----------------------------------+

+----------+-----------------------------------+

在测试环境添加 REL_ DEVID,TASK_ID 组合索引,测试 sql 性能:alter table T_BIOMA_ELOCK_TASK add index idx_REL_DEVID_TASK_ID(REL_DEVID,TASK_ID);

添加索引后执行计划:

这里还要注意一点“隐式转换”:REL_ DEVID 字段数据类型为 varchar,需要在 sql 中加引号:AND T.REL_DEVID = 000000025xxx >> AND T.REL_DEVID = '000000025xxx'

执行时间从 10s+ 降到 毫秒级别:

1 row in set (0.00 sec)

结论

一个典型的 order by 查询的优化,添加更合适的索引可以避免性能问题:执行计划使用索引并不意味着就能执行快。返回搜狐,查看更多

责任编辑:

mysql优化说出九条_技术分享 | MySQL 优化:为什么 SQL 走索引还那么慢?相关推荐

  1. mysql批量添加报错_技术分享 | MySQL 在批量插入时捕捉错误信息

    原创: 杨涛涛 背景 本篇文章来源于今天客户问的一个问题. 问题大概意思是:我正在从 Oracle 迁移到 MySQL,数据已经转换为单纯的 INSERT 语句.由于语句很多,每次导入的时候不知道怎么 ...

  2. mysql 如何设置延迟启动_技术分享 | MySQL 网络延时参数设置建议

    作者:毛思平 工作11年,从事数据库工作7年,主要在金融行业.主要是做oracle,mysql.现在在农行软开中心主要做数据库应用方面的研究. 本文来源:原创投稿 *爱可生开源社区出品,原创内容未经授 ...

  3. bcp 不能调用where 子句_技术分享 || Mysql中IS NULL、IS NOT NULL不能走索引?

    mysql中IS NULL.IS NOT NULL不能走索引? 不知道是啥原因也不知道啥时候, 江湖上流传着这么一个说法 mysql查询条件包含IS NULL.IS NOT NULL.!=.like ...

  4. mysql 行锁 超时_技术分享 | MySQL 行锁超时排查方法优化

    作者:xuty 本文来源:原创投稿 * 爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源. 一.大纲 #### 20191219 10:10:10,234 | com.ali ...

  5. 多少行数_技术分享 | MySQL:查询字段数量多少对查询效率的影响

    作者:高鹏 文章末尾有他著作的<深入理解 MySQL 主从原理 32 讲>,深入透彻理解 MySQL 主从,GTID 相关技术知识. 这个问题是最近一个朋友问我的.刚好就好好看了一下,留下 ...

  6. 发布json数据_技术分享 | MySQL 8.0.17 GA 发布!

    昨日 MySQL 官网正式发布 8.0.17 / 5.7.27 / 5.6.45 三个(维护)版本,距离上一个 GA 版本(8.0.16)发布时隔仅 88 天! MySQL 各开发团队的博客网站,同一 ...

  7. mysql slowlog中querytime分析_技术分享 | Slow Query Log 使用详解

    作者:宓祥康 爱可生交付服务部团队 DBA 擅长日志分析.问题排查等:主要负责处理 MySQL 与我司自研数据库自动化管理平台 DMP 的日常运维问题,对数据库及周边技术有浓厚的学习兴趣. 本文来源: ...

  8. mysql 客户端_技术分享 | MySQL 客户端连不上(1045 错误)原因全解析

    作者:Carlos Tutte.Marcos Albe 翻译:管长龙 在我们学习 MySQL 或从事 MySQL DBA 工作期间,时常会遇到:"我尝试连接到 MySQL 并且收到1045 ...

  9. mysql5驱动_技术分享 | MySQL 8 和 MySQL 5.7 在小型设备的内存消耗分析

    原创: 管长龙 译 作者:Peter Zaitsev 虽然我们经常在较大规模的系统上运行 MySQL ,但我们常常在最小的云实例上运行MySQL,或者只在我们的笔记本电脑上运行它.在这些情况下,MyS ...

最新文章

  1. windows系统杀掉explorer.exe进程后黑屏
  2. The true love
  3. python教程:实现延时回调普通函数的方法
  4. wordpress留言板comments.php添加自定义字段,php – 如何在WordPress / WooCommerce 3中的注释表单中添加自定义字段...
  5. 梯度下降法快速教程 | 第一章:Python简易实现以及对学习率的探讨
  6. matlabrobert锐化_基于Matlab的图像锐化的研究
  7. TensorFlow工作笔记001---Centos7.3下安装TensorFlow最新版本,基于python2.7.5的,没有编译TensorFlow的源码
  8. ORA-00906 missing left parenthesis括号
  9. 如何在ps中调整文字的行距和间距_Wps如何调整文字字符的间距
  10. 【免费毕设】IT产品网上物流管理信息系统的设计与实现(源代码+lunwen)
  11. CentOS系统yum源使用报错:Error: Cannot retrieve repository metadata
  12. java 获取叶子节点_java – 如何获取树的所有叶节点?
  13. 如何在 Mac 上设置 FaceTime?
  14. java 字符串数字验证_验证一个字符串是否由数字组成(Java)
  15. IEC60958/61937协议
  16. Linux运维常见面试题
  17. 计算机语言中str是什么意思,python中str函数的作用是什么
  18. 《亿人帮》与《新米公益》竞品分析报告(简要版)
  19. 爬取淘宝女郎的照片-写给初步入门爬虫的读者
  20. 查看Linux镜像的版本

热门文章

  1. 广告贴——希望大家有空能够参加11月27日的《葵花宝典——WPF自学手册》签名售书活动...
  2. 用Flash创建一个类似Nano War游戏的教程
  3. (oracle)二、创建数据库
  4. ubuntu server 16.10 启用无线网卡
  5. iinflux数据库使用
  6. 使用fullPage做的大图片全屏滚动
  7. Basic:三层架构开发
  8. python数据模型搭建_python之路(19)django数据库模型(model)
  9. 最长回文子串python_最长回文子串(Python)
  10. 利用函数BAPI_PR_CREATE开发采购申请批导