mysql 走索引 很慢_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), xxxSELECT T.TASK_ID,T.xx,T.xx,...FROM T_xx_TASK TWHERE N=NAND 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 = NAND T.REL_DEVID = NAND T.TASK_DATE >= 'S'AND T.TASK_DATE <= 'S'ORDER BY TASK_ID DESCLIMIT 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 TWHERE 1=1AND 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 = 1AND T.REL_DEVID = 000000025xxxAND T.TASK_DATE >= '2019-01-11'AND T.TASK_DATE <= '2019-01-11'ORDER BY TASK_ID DESCLIMIT 0,20;
执行时间 10s+:
1 row in set (10.37 sec)
表索引信息:
show index from T_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) |+------------+---------------------------+| 1161559 | 223 |+------------+---------------------------+
在这个 sql 中 REL_DEVID 字段从命名上看选择度较高,通过下面 sql 来检验确实如此:
select count(*),count(distinct REL_DEVID) from T_BIOMA_ELOCK_TASK;+----------+---------------------------+| count(*) | count(distinct REL_DEVID) |+----------+---------------------------+| 1161559 | 62235 |+----------+---------------------------+
由于有排序,所以得把 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) |+----------+-----------------------------------+| 1161559 | 1161559 |+----------+-----------------------------------+
在测试环境添加 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 查询的优化,添加更合适的索引可以避免性能问题:执行计划使用索引并不意味着就能执行快。
【编辑推荐】
【责任编辑:张燕妮 TEL:(010)68476606】
点赞 0
mysql 走索引 很慢_MySQL 优化:为什么 SQL 走索引还那么慢?相关推荐
- mysql 走索引 很慢_MySQL优化:为什么SQL走索引还那么慢?
背景 2019-01-11 9:00-10:00 一个 MySQL 数据库把 CPU 打满了. 硬件配置:256G 内存,48 core 分析过程 接手这个问题时现场已经不在了,信息有限,所以我们先从 ...
- mysql删除索引很慢_mysql建立索引删除索引很慢的解决_MySQL
bitsCN.com mysql建立索引删除索引很慢的解决 目前情况 建立索引非常慢,需8分钟... 目前环境: ---------------- table行: 30W 版本5.0.45-commu ...
- mysql中b树是什么_MySQL优化中B树索引知识点总结
为什么要进行SQL优化呢?很显然,当我们去写sql语句时: 1会发现性能低 2.执行时间太长, 3.或等待时间太长 4.sql语句欠佳,以及我们索引失效 5.服务器参数设置不合理 SQL语句执行过程分 ...
- mysql 大表更新数据类型_MySQL优化之表结构优化的5大建议(数据类型选择讲的很好)...
殊不知,在N年前被奉为"圣经"的数据库设计3范式早就已经不完全适用了.这里我整理了一些比较常见的数据库表结构设计方面的优化技巧,希望对大家有用. 由于MySQL数据库是基于行(Ro ...
- mysql 多表 三表 删除_mysql 多表join查询索引优化
数据准备 CREATE TABLE IF NOT EXISTS `class` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `card` int( ...
- mysql+index组合索引_MySQL 优化之 index merge(索引合并)
标签: MySQL5.0之前,一条语句中一个表只能使用一个索引,无法同时使用多个索引.但是从5.1开始,引入了 index merge 优化技术,对同一个表可以使用多个索引.理解了 index mer ...
- mysql索引使增删变慢_mysql优化(四)–索引
http://www.cnblogs.com/hustcat/archive/2009/10/28/1591648.html 一. 四种索引类型: 主键索引,唯一索引,全文索引,普通索引 二. 为什 ...
- mysql 设置按天分表_MySQL 优化实战记录
阅读本文大概需要 2 分钟. 背景 本次SQL优化是针对javaweb中的表格查询做的. 部分网络架构图 业务简单说明 N个机台将业务数据发送至服务器,服务器程序将数据入库至MySQL数据库.服务器中 ...
- 优化mysql的21个建议_MySQL优化小建议
MySQL优化小建议 洛逸 发布于 2019-11-04 12:03 背景 "那啥,你过来一下!" "怎么了?我代码都单元测试了的,没出问题啊!"我一脸懵逼跑到 ...
最新文章
- Living in the Matrix with Bytecode Manipulation--转
- 分布式应用架构中的数据传输对象(DTO)
- 包含contains
- ASP.NET实现推送文件到浏览器的方法
- robo 3t连接_使用robo 3t studio 3t连接到地图集
- Java内存模型FAQ(四)重排序意味着什么?
- 【计算机网络复习】1.2.2 OSI参考模型
- 唯美好看的动态个人鹿鸣404单页HTML源码
- 泛型之类型擦除和桥接方法
- oracle客户端查看版本号,Oracle 版本查看及版本号说明
- Linux环境下安装Tableau Server
- 游戏引擎 Unity 的入门易精通难体现在哪?为什么?
- Clipboard.js实现复制文本到剪贴板功能
- 简历职称 计算机,个人简历专业技术职务怎么填 就是你所学的专业技术是你取得...
- 计算机的业务流程图是什么意思,什么是业务流程图?它的作用是什么?
- h3c 云服务器操作系统,产品技术-H3C CloudOS云操作系统电信版-新华三集团-H3C
- 红米开发版刷机教程_红米Note3开发版怎么刷机 红米Note3开发版刷机教程
- Disparity Map
- Pyhton之模拟石头剪子布游戏篇
- 文献管理与信息分析2023春课程随堂测验答案