记一次Mysql慢SQL优化过程

那年烟雨落申城
• 阅读 168

缘起

最近有个同事让我看看一个测试环境的SQL,因为这个SQL执行了几十秒,导致接口超时了。 sql为(里面表名已经使用test_table开头的表名脱敏,返回的字段使用*脱敏,别名未修改):

select
    *
from
    test_table1 e
join test_table2 ebp on
    ebp.event_id = e.id
    and ebp.is_deleted = 'N'
    and e.id in (1260,1294,1297,1300,
    1520,1727,1730,1731,1820,1897,
    1898,1899,1900,2542,2543,2920,
    3137,3140,4843,4858,4942,5014,
    5077,5324,5337,5536,5550,5556,
    5557,5558,5560,5563,5564,5567,
    5568,5797,5798,5845,6645)
    and e.is_deleted = 'N'
    and e.is_display = '1'
join (
    select
        *
    from
        test_table3 p
    left join test_table4 a on
        p.attr_id = a.id
    where
        p.is_deleted = 'N'
        and a.is_deleted = 'N' ) tcp on
    ebp.buried_point_type = tcp.buried_point_type

过程

先使用EXPLAN看下: 记一次Mysql慢SQL优化过程 我们来回顾下这几个字段代表的意义:

type 访问类型 以下类型性能从差到优: all:全表扫描,这个代价是最大的,性能最差的,有很大的优化空间 index:全索引扫描,扫描所有索引,比全表扫描快一点 range:: 范围查询,这个范围一定是用了索引的范围,大多出现在>,<,>=,<=,BWTWEEN这种带范围的查询 ref:结果是使用了索引的(非主键索引,非唯一索引),我们知道索引是有序的,即使是这个索引有重复字段,也会在一个小范围内并且连续,不会全表扫描 ref_eq:使用了索引结果只有一个,出现在查询用到了唯一索引或主键索引的场景 const:当前查询使用了主键索引

Extra 额外访问类型: using where 使用where条件进行了数据过滤,一般遇到这个很难判定性能好坏,需要和type一起判断是否需要优化 using index 使用了索引,一般来讲结果都在一颗索引树上查到(无需回表),性能较好,一般不需要优化 using index condition 使用了索引,但是需要回表,此时优化一般是使用覆盖索引 using filesort 使用了临时文件进行排序,一般出现在order by的排序中由于排序字段没加索引导致全部数据排序,并且没法再内存中完成排序,性能较差,一般会在排序字段上加索引,避免全部排序 using temporary 使用了临时表,性能较差,一般出现在``group by和order by`一起的时候,分组字段和排序字段不一致导致需要中渐变暂存结果,优化方案一般是加索引 using join buffer (Block Nested Loop) 需要循环计算,一般出现在两个表join操作但是join的字段没有加索引导致,一般优化方案是在join的字段上加索引

possible keys 可能使用的索引

key 实际使用到的索引

ref 查询中使用到的与其它表的关联字段,外键等

以上引用来自我的其它博客 mysql慢日志分析,执行计划

可以看到type有两个ALL,这个大概就是最慢的两个点了,去看下为啥会全表扫描,发现这个表只有一个主键id是主键索引,但是 表连接ebp.event_id = e.id中的ebp.event_id却没有索引 记一次Mysql慢SQL优化过程 这还怎么玩?没有索引大概就是全表扫描的原因了。再看另一个typeALL的表,是一样的,也是只有一个主键建了索引,p.attr_id = a.id中的attr_id根本没索引。把上面两个字段加上索引试下。 记一次Mysql慢SQL优化过程 可以看到ebp这个表的type已经不是ALL了,但是p还是ALL,p是ALL的原因应该是attr_id虽然加了索引,但是走这个索引的话要全量回表去取所有字段,就不如直接全量扫描更快了,起码省略了扫描索引并回表操作了。如果取得不是全部字段,可以考虑使用覆盖索引优化

总结

讲道理的话,连结查询的字段一定是要加索引的,这个表因为历史原因没有加,最开始我感觉这玩意儿正常人都会加吧,结果就是没有,可能当时设计人员认为没有很多数据吧。历史问题就不追究了,主要是要知道怎么去定位问题和解决问题。

点赞
收藏
评论区
推荐文章
Easter79 Easter79
2年前
sql注入
反引号是个比较特别的字符,下面记录下怎么利用0x00SQL注入反引号可利用在分隔符及注释作用,不过使用范围只于表名、数据库名、字段名、起别名这些场景,下面具体说下1)表名payload:select\from\users\whereuser\_id1limit0,1;!(https://o
peter peter
3年前
mysql 修改表或表结构常用sql语句
批量修改表名的sql语句altertableold_namerenamenew_name;修改表名altertabletestaddcolumnadd_namevarchar(10);添加表列altertabletestdropcolumndel_name;删除表列altertabletestm
Wesley13 Wesley13
2年前
mysql中时间比较的实现
MySql中时间比较的实现unix\_timestamp()unix\_timestamp函数可以接受一个参数,也可以不使用参数。它的返回值是一个无符号的整数。不使用参数,它返回自1970年1月1日0时0分0秒到现在所经过的秒数,如果使用参数,参数的类型为时间类型或者时间类型的字符串表示,则是从1970010100:00:0
Wesley13 Wesley13
2年前
mysql5.6 分页查询优化
mysql5.6分页查询优化场景:表结构:主键(非自增)contentCode(varchar),过滤条件列为updateTime(timeStamp),已经为timestamp建立索引。搜索sql为:SELECTFROMmy_hello_tableWHEREupdat
Wesley13 Wesley13
2年前
Oracle一张表中实现对一个字段不同值和总值的统计(多个count)
需求:统计WAIT\_ORDER表中的工单总数、未处理工单总数、已完成工单总数、未完成工单总数。表结构:为了举例子方便,WAIT\_ORDER表只有两个字段,分别是ID、STATUS,其中STATUS为工单的状态。1表示未处理,2表示已完成,3表示未完成总数。 SQL:  1.SELECT   2
Wesley13 Wesley13
2年前
MySQL清空表漏洞!
MySQL有一个特点,当某个字段是字符串时,如果你的sql传数字它会尝试把这一列所有值转换成数字进行匹配,如果不是数字则会转换为0.创建表test,并插入测试数据CREATETABLEtest(idvarchar(10)NOTNULL,PRIMARYKEY(id));
Stella981 Stella981
2年前
Hibernate纯sql查询结果和该sql在数据库直接查询结果不一致
问题:今天在做一个查询的时候发现一个问题,我先在数据库实现了我需要的sql,然后我在代码中代码:selectdistinctd.id,d.name,COALESCE(c.count_num,0),COALESCE(c.count_fix,0),COALESCE(c
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
京东云开发者 京东云开发者
6个月前
线上SQL超时场景分析-MySQL超时之间隙锁 | 京东物流技术团队
前言之前遇到过一个由MySQL间隙锁引发线上sql执行超时的场景,记录一下。背景说明分布式事务消息表:业务上使用消息表的方式,依赖本地事务,实现了一套分布式事务方案消息表名:mqmessages数据量:3000多万索引:createtime和statuss