记一次生产慢sql索引优化及思考 | 京东云技术团队

京东云开发者
• 阅读 383

一 问题重现

夜黑风高的某一晚,突然收到一条运营后台数据库慢sql的报警,耗时竟然达到了60s。

记一次生产慢sql索引优化及思考 | 京东云技术团队

看了一下,还好不是很频繁,内心会更加从容排查问题,应该是特定条件下没有走到索引导致,如果频繁出现慢查询,可能会将数据库连接池打满,导致数据库不可用,从而导致应用不可用。

二 问题排查

报警自带定位慢sql语句,这个是很早就上线的一条sql语句,下面对sql语句进行了简化:

select * from xxx where gear_id=xxx and examine=xxx order by id desc limit 10,这是个简单的根据流量池gear_id查询,按照主键id倒序进行分页查询10条数据的语句。

在examine=2时查询速度很快,但是在examine=3时,查询速度极慢,然后分别在不同的examine下查看执行计划,得到的执行计划都是一致的。

记一次生产慢sql索引优化及思考 | 京东云技术团队

查看执行计划,发现possible_keys中有idx_gear_id索引,但是实际用到的key却是PRIMARY,并且extra中明确用了where条件进行数据过滤。到现在就明白了这个sql是在主键聚簇索引上进行扫描,然后用where语句条件进行过滤,时间耗费在这了。

这个也解释了为什么examine在不同状态下的耗时不一样,取决于where过滤扫描的行数,扫描行数越多,执行越慢,但同一个问题是都没走到我们已有的索引idx_gear_id。

当单表数量较小时,无论有没有索引,或者走主键索引扫描或者普通索引都很快,很容易忽略这些问题,此时的表现就是你好,我好,大家好,然后随着数据量的增大,当达到千万级别或者亿级时,慢查询的问题就凸显出来了。

三 原理剖析

为什么mysql会选择这个不合适的主键聚簇索引?

以常用的InnoDb存储引擎为例,看一下聚簇索引和非聚簇索引查询区别:

聚簇索引:通常就是按照每张表的主键构造一颗B+树,叶子节点中存放的就是整张表的行记录数据,即数据和主键都在索引上

非聚簇索引:表的二级索引字段(比如唯一索引,联合索引等)构造的一颗B+树,叶子节点存储的是Key字段+主键值,即非聚集索引的叶节仍然是索引节点,但它有一个指向最终数据索引的指针。

聚簇索引查询原理:

记一次生产慢sql索引优化及思考 | 京东云技术团队

非聚簇索引查询原理(二级索引查询):

记一次生产慢sql索引优化及思考 | 京东云技术团队

由以上的索引数据结构可以看出,因为聚簇索引将索引和数据保存在同一个B+树中,因此通常从聚簇索引中获取数据比非聚簇索引更快,而非聚簇索引在获取到叶子节点的主键后,需要再次查询主键索引,即回表查询行记录数据。当然如果查询的列只是索引字段,比如查询姓名和年龄,可以创建联合索引,即索引存储的内容即为需要查询的内容,这种查询速度往往比主键索引更快,这种索引查询又称为覆盖索引。

什么是回表?

将以上的索引数据映射成常见的用户表user的索引为例,上面的聚簇索引就是以id字段为主键的索引,name字段为非聚簇索引,还有age等其他表字段是非索引字段,示例sql:select * from user where id = 1; 这条 SQL 语句就不需要回表。原因是根据主键的查询方式,则只需要搜索 id聚簇索引这棵 B+ 树,就可以查到对应的数据。

但当我们使用非聚簇索引 name 这个索引来查询 name = b 的记录时就要用到回表。原因是通过 name 这个二级索引查询方式,则需要先搜索 name 索引树,然后得到主键 id,即PK的值为 1,再到主键id聚簇索引树再搜索一次。这种根据二级索引查询到主键id,再根据主键id查询主键聚簇索引的过程就称为回表。

回到为什么mysql会选择这个不合适的主键聚簇索引问题本身,mysql执行器认为使用二级索引查出来的数据太多了,还需要基于磁盘做临时存储进行排序,然后排序取出10条,然后进行回表查询字段,性能可能会很差,所以采用了直接采用了按顺序扫描主键聚簇索引,和where条件gear_id=xxx and examine=xxx进行对比,最多放10条即可,这种情况就是数量小的时候没问题,但是当数据量大的时候,就需要一直扫描所有的数据,直到查到符合where条件的10条数据为止,同时耗时也急剧增长。

四 解决问题

为了快速解决问题,可以采用强制索引force index,即在写sql语句时指定使用具体的索引

sql示例 :select * from xxx force index (idx_gear_id) where gear_id=xxx and examine=3 order by id desc limit 10,强制使用idx_gear_id这个索引。

以下为使用强制索引的执行计划:

记一次生产慢sql索引优化及思考 | 京东云技术团队

可以看到实际使用的索引key就是idx_gear_id,执行耗时在几百毫秒,运营后台的业务人员完全可接受。

五 长期优化

由于表的数据越来越多,查询条件错综复杂,还有用json字段查询问题,决定将数据异构到es查询,将json字段打平,es天然支持复杂的查询条件,查询响应更快。

es数据同步方案:

记一次生产慢sql索引优化及思考 | 京东云技术团队

在ES数据同步链路中,通过京东科技中间件DTS监听数据库的binlog,将索引字段(查询条件字段)及业务唯一id写入ES。

在业务运营查询时,根据复杂的查询条件,先去ES查询,将业务唯一id查出,再根据业务唯一id去DB中查询业务明细数据,同时解决了业务查询的复杂性和查询性能。

作者:京东科技 张石磊

来源:京东云开发者社区 转载请注明来源

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
mysql配置调优
工作中,会遇到需要查看mysql的top20慢sql,逐个进行优化,加上必要的索引这种需求,这时就需要开启数据库的慢查询日志的功能1.查询当前慢查询日志的状态\默认为关闭状态mysqlshowvariableslike"
Stella981 Stella981
3年前
PostgreSQL死锁进程及慢查询处理
1、死锁进程查看:SELECTFROMpg_stat_activityWHEREdatname'数据库名称'andwaitingtrue;pid进程id。2、慢查询SQL:selectdatname,pid,usename,application_name,client_addr,client
Wesley13 Wesley13
3年前
Mysql 执行计划各列释义
在排查编写Mysql查询语句时,除了需要满足业务条件,还需要考虑所编写SQL的性能表现,避免出现慢SQL导致大量慢查询的情况。通常,可以通过查看执行计划的方式查看所编写SQL语句的性能优劣。此外,还可以通过查看语句的分阶段执行的时间、操作消耗来进行补充分析。1\.执行计划的列1.1.id列查询的序号1.2.s
Wesley13 Wesley13
3年前
Mysql占用过高CPU时的优化手段
慢查询日志,将那些执行时间过长且占用资源过多的SQL拿来进行explain分析,导致CPU过高,多数是GroupBy、OrderBy排序问题所导致,然后慢慢进行优化改进。比如优化insert语句、优化groupby语句、优化orderby语句、优化join语句等等;3)考虑定时优化文件及索引;4)定期分析表,使用optimizetable;
Stella981 Stella981
3年前
LVS+Keepalived负载均衡实践与心得补遗
一.实践背景,分析:公司研发的业务系统某个功能频繁出现数据库Mysql性能问题,导致系统使用卡顿,响应慢,且数据库所在服务器CPU负载居高不下,影响其他项目系统的正常数据库访问和使用。除去研发人员优化sql工作外,作为运维人员可以尝试对当前服务架构改造,目前架构大致如下:!(https://static.oschina.net/uploads/i
Mybatis-SQL分析组件 | 京东云技术团队
大促备战,最大的隐患项之一就是慢sql,带来的破坏性最大,也是日常工作中经常带来整个应用抖动的最大隐患,而且对sql好坏的评估有一定的技术要求,有一些缺乏经验或者因为不够仔细造成一个坏的sql成功走到了线上,等发现的时候要么是造成了线上影响、报警、或者后置的慢sql采集发现,这时候一般无法快速止损,需要修改代码上线、或者调整数据库索引。
慢SQL原因分析之索引失效 | 京东物流技术团队
现象最近收到一个慢sql工单,慢sql大概是这样:“selectxxxfromtabelwheretype1”。咦,type字段明明有索引啊,为啥是慢sql呢?原因通过执行explain,发现实际上数据库执行了全表扫描,从而被系统判定为慢sql。这时有一定
京东云开发者 京东云开发者
10个月前
火眼金睛破局ES伪慢查询 | 京东物流技术团队
一、问题现象服务现象服务接口的TP99性能降低ES现象YGC:耗时极其不正常,峰值200次,耗时7sFULLGC:不正常,次数为1但是频繁,STW5s慢查询:存在慢查询5二解决过程1、去除干扰因素从现象上看应用是由于某种原因导致JVM内存使用率不断增
京东云开发者 京东云开发者
6个月前
Sql优化之回表
前言:MySQL的性能是大家在使用时十分关心的问题,比如在高并发访问时,并且有慢sql存在的情况下,MySQL的性能会明显下降,这会导致数据库响应时间变慢,甚至导致数据库宕机。那么为了避免Mysql性能问题,比较常用的方式创建适当的索引,提升sql语句的执