• mysql优化案例分析


          本文总结了一些工作常见的sql优化例子,虽然比较简单,但很实用,希望对大家有所帮助。sql优化一般分为两类,一类是sql本身的优化,如何走到合适的索引,如何减少排序,减少逻辑读;另一类是sql本身没有优化余地,需要结合业务场景进行优化。即在满足业务需求的情况下对sql进行改造,已提高sql执行速度,减少响应时间。

    例子1:

    SELECT ID FROM SENDLOG WHERE TO_DAYS(NOW())-TO_DAYS(GMT_CREATE) > 7;

    问题:对索引列GMT_CREATE进行了运算,无法使用索引

    优化后sql:
    select id from sendlog where gmt_create < now() - 7
     
    例子2:

    SELECT * FROM SENDLOG where result = 1 and gmt_create > '2013-10-29 12:40:44' limit 2000;

    问题:result列含有索引,但mysql执行计划没有用到。

    分析:result类型为char,传递的值1为整型,数据类型不一致,导致没法用索引,对于时间类型gmt_create > '2013-10-29 12:40:44',可以直接使用。

    优化后sql:

    SELECT * FROM SENDLOG where result = '1' and gmt_create > '2013-10-29 12:40:44' limit 2000;

    例子3:

    场景:获取某个卖家未读的消息。

    select count(*) from mc_msg where receiver='sun098' and status='UNREAD' and title is not null;

    问题:有时候db负载飙高,sql响应时间变慢。

    分析:导致db负载飙高的原因是多个大卖家并发查询的时,cpu和逻辑读增加,load飙高。由于receiver,status已有索引,sql本身已经没有优化空间,了解业务后发现其实业务不需要精确值,如果大于99条,页面就直接显示为99+

    优化后sql

    select count(*) from (select id from mc_msg where receiver='sun098' and status='UNREAD' and title is not null limit 100) a;

    例子4:

    场景:查看历史订单留言记录,未读留言的放在前面,已读的放在后面,并且按时间递减排序

    select * from(
    select ID,GMT_CREATE,GMT_MODIFIED,SENDER_ALI_ID,RECEIVER_ALI_ID,UNREAD_COUNT,STATUS,LAST_MESSAGE_ID,RELATION_ID,SELLER_ADMIN_SEQ,IS_READ 
    from message_relation_sender
    WHERE SENDER_ALI_ID = 119545671  and UNREAD_COUNT > 0 
    order by LAST_MESSAGE_ID desc) m 
    union all 
    select * from(
    select ID,GMT_CREATE,GMT_MODIFIED,SENDER_ALI_ID,RECEIVER_ALI_ID,UNREAD_COUNT,STATUS,LAST_MESSAGE_ID,RELATION_ID,SELLER_ADMIN_SEQ,IS_READ from message_relation_sender
    WHERE SENDER_ALI_ID = 119545671  and UNREAD_COUNT = 0 
    order by LAST_MESSAGE_ID desc) n limit 5000,15;
     
    分析:
    (1)unread_count表示未读的订单留言记录数目;
    (2)第一个子查询获取未读留言记录,第二子查询获取已读留言记录;
    (3)LAST_MESSAGE_ID 递增,最新的订单留言,LAST_MESSAGE_ID最大。
    (4)limit 5000,15是分页查询
    这里使用union all,主要特点在于union all 不对结果集排序,直接合并,达到了“未读留言的放在前面,已读的放在后面”的效果,但同时也造成了两次扫描索引的结果,每个子查询都需要排序;而且union all还会产生临时表,执行代价会更大。
     
    优化:
    这里看到unread_count实际值对这个查询没有实际意义,我们只需要区分已读和未读即可。由于sql本身已经没有优化余地,考虑对表结构进行修改,加一个字段is_read,表示已读和未读。is_read=2表示未读;is_read=1表示已读。通过组合索引(SENDER_ALI_ID,is_read, LAST_MESSAGE_ID),既可以完成过滤,还可以完成排序。
     
    优化后sql:
    select ID,GMT_CREATE,GMT_MODIFIED,SENDER_ALI_ID,RECEIVER_ALI_ID,UNREAD_COUNT,STATUS,LAST_MESSAGE_ID,RELATION_ID,SELLER_ADMIN_SEQ,IS_READ from message_relation_sender
    where SENDER_ALI_ID = 119545671  order by is_read desc, LAST_MESSAGE_ID desc limit 5000,15
     
    第二次优化:
    由于索引不包含所有的返回字段,因此需要回表,而mysql对于limit 5000,15的查询却需要返回5015次,这种无效的返回很影响查询效率。

    分页的优化写法:

    select t1.ID,GMT_CREATE,GMT_MODIFIED,SENDER_ALI_ID,RECEIVER_ALI_ID,UNREAD_COUNT,STATUS,LAST_MESSAGE_ID,RELATION_ID,SELLER_ADMIN_SEQ,IS_READ from message_relation_sender t1,
    (select id 
     from message_relation_sender
     where SENDER_ALI_ID = 119545671 order by is_read desc, LAST_MESSAGE_ID desc limit 5000,15
    )t2 where t1.id = t2.id
     
    分析:由于id是主键,不需要回表,通过连接查询,最终只需要15次回表即可。

       

  • 相关阅读:
    Python random模块下的常用函数小结
    MySQL 数据库连接
    LeetCode 376. 摆动序列 做题小结
    LeetCode 1005. K 次取反后最大化的数组和 做题小结
    LeetCode 455. 分发饼干 做题小结
    完美解决Python与anaconda之间的冲突问题
    LeetCode 122. 买卖股票的最佳时机 II 做题小结
    LeetCode 714. 买卖股票的最佳时机含手续费 做题小结
    如何去除有道云笔记广告(windows)
    baby web
  • 原文地址:https://www.cnblogs.com/cchust/p/3444510.html
Copyright © 2020-2023  润新知