0%

慢SQL优化-order by id limt 1

前言

今天线上爆出来一条Mysql的慢sql,好家伙还没切库呢,慢SQL优化先安排上了,醉了。该SQL已经在线上运行了大半个月了,现在才爆出来,挺有学习价值的。

SQL

SELECT * FROM record WHERE (shop_id = ? AND user_id = '?' AND car_id = '?' AND status IN ('?') AND create_time >= '?' AND create_time < '?') ORDER BY id DESC limit 1;

问题分析

  1. 第一次爆出来的时候,是因为cat出现了CommunicationsException错误。

    CommunicationsException异常可能原因

    If you get a SQLException: Connection refused or Connection timed out or a MySQL specific CommunicationsException:
    Communications link failure, then it means that the DB isn’t reachable at all. This can have one or more of the following causes:
    1.IP address or hostname in JDBC URL is wrong.
    2.Hostname in JDBC URL is not recognized by local DNS server.
    3.Port number is missing or wrong in JDBC URL.
    4.DB server is down.
    5.DB server doesn’t accept TCP/IP connections.
    6.DB server has run out of connections.
    7.Something in between Java and DB is blocking connections, e.g. a firewall or proxy or others

    To solve the one or the other, follow the following advices:

    1.Verify and test them with ping.
    2.Refresh DNS or use IP address in JDBC URL instead.
    3.Verify it based on my.cnf of MySQL DB.
    4.Start the DB.
    5.Verify if mysqld is started without the –skip-networking option.
    6.Restart the DB and fix your code accordingly that it closes connections in finally.
    7.Disable firewall and/or configure firewall/proxy to allow/forward the port.

看了下具体报错是由于Mysql连接池与java服务超时了。极有可能是慢sql查询超时导致的连接断掉了。
  1. 排查日志,elk上拿到sql参数explain了下,执行计划如下:

    possible_keys有:biz索引,create_time索引,userId索引,status索引。走了索引的呀,多次执行换了下参数都能命中userId或者create_time索引。
  2. 从RDS上拿到了该sql的执行情况,信息有这么几个:大多数执行时间不会超过100ms,扫描行数为500左右;而极端的慢sql会执行14s,扫描行数是300w+(也就是全表扫描)。结合上面的执行情况可以知道极端sql是没有走到possible_keys中的索引,因为命中这些索引都不会走全表。另外注意到返回行数是0。

  3. 由于是单表查询sql,排除了where条件中能命中的索引,那么只会是因为order by id的影响,由于排序消耗资源是非常大的,而主键索引是有序的,所以优化器选择走了主键索引,去判断where条件是否命中,如果一直找不到符合条件的数据,那么mysql就会全表扫描。

SQL优化

这种情况下为了避免sql走到主键索引,并结合业务场景(只会查询当天的数据),将order by id改为order by create_time。

后续分析

后续又出现了这样的情况,及时拿到数据去explain了,确实是如分析所说的走到了主键索引。并且如果调大limit的数值,或者将时间参数往前调几天都没有走主键索引,又回头看了下出错的数据,都是测试门店测试用户(单人下会有很多的数据)。
所以走到主键索引的条件还是很苛刻的,时间范围需要是当天的,这样走主键索引可以直接倒序往前查,limit的数量要小,这样优化器会判断这样的条件比较好命中。另外用户数据倾斜也会影响优化器,认为这样的条件下很容易命中,不会全表扫描。

参考文章

https://tech.meituan.com/2014/06/30/mysql-index.html