先看一段sql:

  1. <span style="font-size:18px;">SELECT
  2. *
  3. FROM
  4. rank_user AS rankUser
  5. LEFT JOIN rank_user_level AS userLevel ON rankUser.id = userLevel.user_id
  6. LEFT JOIN rank_product AS product ON userLevel.new_level = product.level_id
  7. LEFT JOIN rank_product_fee AS fee ON userLevel.fee_id = fee.fee_id
  8. LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id
  9. ORDER BY
  10. rankUser.create_time DESC
  11. LIMIT 10 OFFSET 0</span>
<span style="font-size:18px;">SELECT
*
FROM
rank_user AS rankUser
LEFT JOIN rank_user_level AS userLevel ON rankUser.id = userLevel.user_id
LEFT JOIN rank_product AS product ON userLevel.new_level = product.level_id
LEFT JOIN rank_product_fee AS fee ON userLevel.fee_id = fee.fee_id
LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id
ORDER BY
rankUser.create_time DESC
LIMIT 10 OFFSET 0</span>

介绍一下这段sql的表的构成:一张主表:rank_user;两张跟rank_user直接关联(多张表通过同一字段最好是主键进行关联)的表:rank_user_level ,rank_user_login_stat ;两张跟rank_user非直接关联的表:rank_product ,rank_product_fee 。这段sql看似简单,但是执行时间却很长,我们来看一下执行计划:

执行时间1.45s,可以看到,这段不仅仅扫描全表,而且使用了临时表,进行了文件排序。

为了找到原因,我们把排序去掉看一下:

  1. SELECT
  2. *
  3. FROM
  4. rank_user AS rankUser
  5. LEFT JOIN rank_user_level AS userLevel ON rankUser.id = userLevel.user_id
  6. LEFT JOIN rank_product AS product ON userLevel.new_level = product.level_id
  7. LEFT JOIN rank_product_fee AS fee ON userLevel.fee_id = fee.fee_id
  8. LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id
  9. -- ORDER BY
  10. -- rankUser.create_time DESC
  11. LIMIT 10 OFFSET 0
SELECT
*
FROM
rank_user AS rankUser
LEFT JOIN rank_user_level AS userLevel ON rankUser.id = userLevel.user_id
LEFT JOIN rank_product AS product ON userLevel.new_level = product.level_id
LEFT JOIN rank_product_fee AS fee ON userLevel.fee_id = fee.fee_id
LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id
-- ORDER BY
-- rankUser.create_time DESC
LIMIT 10 OFFSET 0

执行时间0.015s,扫描行数67452,果然是排序惹的祸。但是仅仅是排序惹的祸吗?别忘了这里有两张非直接关联的表,这样的查询,如果有查询条件或者排序分组的时候往往都需要创建临时表(这个没有办法,想想也知道)。为了验证这个观点,我们把两张非直接关联的表去掉看一下:

  1. SELECT
  2. *
  3. FROM
  4. rank_user AS rankUser
  5. LEFT JOIN rank_user_level AS userLevel ON rankUser.id = userLevel.user_id
  6. -- LEFT JOIN rank_product AS product ON userLevel.new_level = product.level_id
  7. -- LEFT JOIN rank_product_fee AS fee ON userLevel.fee_id = fee.fee_id
  8. LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id
  9. ORDER BY
  10. rankUser.create_time DESC
  11. LIMIT 10 OFFSET 0
SELECT
*
FROM
rank_user AS rankUser
LEFT JOIN rank_user_level AS userLevel ON rankUser.id = userLevel.user_id
-- LEFT JOIN rank_product AS product ON userLevel.new_level = product.level_id
-- LEFT JOIN rank_product_fee AS fee ON userLevel.fee_id = fee.fee_id
LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id
ORDER BY
rankUser.create_time DESC
LIMIT 10 OFFSET 0



执行时间0.003s,扫描行数10,屌爆了有木有,mysql多表直接关联在没有其他筛选条件的情况下,查询速度大大提升,而且排序可以使用create_time这个索引,直接取到前十条。

到了这里,我想大家应该已经明白第一条sql查询时间很长的原因了:多表非直接关联的前提下还要排序。mysql查询往往最需要优化的地方就是临时表和文件排序了。这里总结一下教训:

1.mysql查询存在直接关联和非直接关联的问题,这两种查询效率差别很大;

2.mysql排序尽量使用索引;

3.mysql多表关联left join其他表的时候,如果以其他表的字段作为查询条件都会产生临时表;

4.mysql在非直接关联的基础上进行排序会很慢,需要进行优化;

知道了问题,我们就好优化了,这里我给出了两种方案:

第一种(子查询,适合子查询部分不作为查询条件):

  1. SELECT
  2. rankUser.id, rankUser.qq, rankUser.phone, rankUser.regip, rankUser.channel, rankUser.create_time, rankUser.qudao_key, rankUser.qq_openid, rankUser.wechat_openid,
  3. userLevel.recommend_count,userLevel.end_time,userLevel.new_level,userLevel.`level`,userLevel.new_recommend_count,userLevel.`is_limited`,
  4. (case when userLevel.new_level > 1 then 1 else 0 end) is_official_user,
  5. (select product_name from rank_product where level_id = userLevel.new_level) product_name,
  6. (select period from rank_product_fee where fee_id = userLevel.fee_id) period,
  7. userLoginInfo.last_login, userLoginInfo.login_count, userLoginInfo.login_seconds
  8. FROM rank_user AS rankUser
  9. LEFT JOIN rank_user_level as userLevel on userLevel.user_id=rankUser.id
  10. LEFT JOIN rank_user_login_stat as userLoginInfo ON rankUser.id = userLoginInfo.user_id
  11. ORDER BY
  12. rankUser.create_time DESC
  13. LIMIT 10 OFFSET 0
SELECT
rankUser.id, rankUser.qq, rankUser.phone, rankUser.regip, rankUser.channel, rankUser.create_time, rankUser.qudao_key, rankUser.qq_openid, rankUser.wechat_openid,
userLevel.recommend_count,userLevel.end_time,userLevel.new_level,userLevel.`level`,userLevel.new_recommend_count,userLevel.`is_limited`,
(case when userLevel.new_level > 1 then 1 else 0 end) is_official_user,
(select product_name from rank_product where level_id = userLevel.new_level) product_name,
(select period from rank_product_fee where fee_id = userLevel.fee_id) period,
userLoginInfo.last_login, userLoginInfo.login_count, userLoginInfo.login_seconds
FROM rank_user AS rankUser
LEFT JOIN rank_user_level as userLevel on userLevel.user_id=rankUser.id
LEFT JOIN rank_user_login_stat as userLoginInfo ON rankUser.id = userLoginInfo.user_id
ORDER BY
rankUser.create_time DESC
LIMIT 10 OFFSET 0

第二种(非直接关联转变成直接关联,这个要根据业务来定,我这里rank_product和rank_product_fee两张表只是为了查询rank_user_level表中的产品和产品费用信息,而rank_user_level是一张直接关联的表,故这里可以先将这三张表进行合并,然后再和rank_user表进行联合查询):

  1. SELECT
  2. *
  3. FROM
  4. rank_user AS rankUser
  5. LEFT JOIN (
  6. select
  7. l.*,p.product_name,f.period
  8. from
  9. rank_user_level l,rank_product p,rank_product_fee f
  10. where
  11. l.new_level = p.level_id
  12. and l.fee_id = f.fee_id
  13. ) AS userLevel ON rankUser.id = userLevel.user_id
  14. LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id
  15. ORDER BY
  16. rankUser.create_time DESC
  17. LIMIT 10 OFFSET 0
SELECT
*
FROM
rank_user AS rankUser
LEFT JOIN (
select
l.*,p.product_name,f.period
from
rank_user_level l,rank_product p,rank_product_fee f
where
l.new_level = p.level_id
and l.fee_id = f.fee_id
) AS userLevel ON rankUser.id = userLevel.user_id
LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id
ORDER BY
rankUser.create_time DESC
LIMIT 10 OFFSET 0

mysql查询优化--临时表和文件排序(Using temporary; Using filesort问题解决)的更多相关文章

  1. MYSQL 磁盘临时表和文件排序

    因为Memory引擎不支持BOLB和TEXT类型,所以,如果查询使用了BLOB或TEXT列并且需要使用隐式临时表,将不得不使用MyISAM磁盘临时表,即使只有几行数据也是如此. 这会导致严重的性能开销 ...

  2. MySQL查询优化之explain的深入解析

    在分析查询性能时,考虑EXPLAIN关键字同样很管用.EXPLAIN关键字一般放在SELECT查询语句的前面,用于描述MySQL如何执行查询操作.以及MySQL成功返回结果集需要执行的行数.expla ...

  3. MySQL查询优化(转)

    在分析性能欠佳的查询时,应考虑: 1) 应用程序是否正获取超过需要的数据,即访问了过多的行或列. 2) Mysql服务器是否分析了超过需要的行. 如果发现访问的数据行数很大,而生成的结果中数据行很少, ...

  4. 一文带你掌握MySQL查询优化技能

    查询优化本就不是一蹴而就的,需要学会使用对应的工具.借鉴别人的经验来对SQL进行优化,并且提升自己. 分享一套博主觉得讲的很详细很实用的MySQL教程给大家,可直接点击观看! https://www. ...

  5. mysql中的文件排序(filesort)

    在MySQL中的ORDER BY有两种排序实现方式: 1. 利用有序索引获取有序数据 2. 文件排序 在explain中分析查询的时候,利用有序索引获取有序数据显示Using index ,文件排序显 ...

  6. MySQL查询优化:连接查询排序limit

    MySQL查询优化:连接查询排序limit(join.order by.limit语句) 2013-02-27      个评论       收藏    我要投稿   MySQL查询优化:连接查询排序 ...

  7. mysql查询优化之一:mysql查询优化常用方式

    一.为什么查询速度会慢? 一个查询的生命周期大致可以按照顺序来看:从客户端,到服务器,然后在服务器上进行解析,生成执行计划,执行,并返回结果给客户端.其中在“执行”阶段包含了大量为了检索数据到存储引擎 ...

  8. MySQL系统临时表、用户临时表

    MySQL临时表分为系统使用的临时表和用户使用的临时表. 系统使用的临时表是指MySQL在执行某些SQL语句时需要依赖临时表来完成整个过程.系统使用的临时表的情况可以分为以下几种: *  group ...

  9. php面试专题---18、MySQL查询优化考点

    php面试专题---18.MySQL查询优化考点 一.总结 一句话总结: 慢查询:查找分析查询速度慢的原因 数据访问:优化查询过程中的数据访问 长难句:优化长难的查询语句 特定类型:优化特定类型的查询 ...

随机推荐

  1. 安卓实现序列化之Parcelable接口

    安卓实现序列化之Parcelable接口 1.实现序列化的方法: Android中实现序列化有两个选择:一是实现Serializable接口(是JavaSE本身就支持的) .一是实现Parcelabl ...

  2. JavaScript大数组如何根据对象的key快速找到并删除

    查找:上代码. function isBigEnough(element) { return element >= 15; } var ret1 = [12, 5, 8, 130, 44].fi ...

  3. android 更新ui

    https://www.cnblogs.com/rayray/p/3437048.html https://www.cnblogs.com/zhaoyanjun/p/5546683.html

  4. Swift3.0中关于日期类的使用指引

    日期的处理在大大小小的iOS项目中都十分常见,随着Swift3.0正式版的即将推出,语法的改变让NSDate以及相关类的使用都与之前略有不同,这里将会对基于Swift3.0版本的NSDate及相关类的 ...

  5. php简单表格函数

    php简单表格函数 代码 <?php //ctrl+shift+j /** * @param unknown $rows * @param unknown $cols * @param stri ...

  6. suse linux通过iso文件安装gcc

    mount -t iso9660 -o loop SLES-11-SP4-DVD-x86_64-GM-DVD1.iso /media/#仅仅上述iso1即可 不需要mount iso2 mount - ...

  7. 【NOIP 2011】 Mayan游戏

    [题目链接] https://www.luogu.org/problemnew/show/P1312 [算法] 深度优先搜索 [代码] #include<bits/stdc++.h> us ...

  8. [jzoj 6073] 河 解题报告 (DP)

    interlinkage: https://jzoj.net/senior/#main/show/6073 description: solution: 考虑一条河$x$被染的效果 显然对于一条河$i ...

  9. NOIP2013T1 转圈游戏 快速幂

    描述 n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏.按照顺时针方向给 n 个位置编号,从0 到 n-1.最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置, --, 依此 ...

  10. guice基本使用,三种注入方式(二)

    guice提供了强大的注入方式. 1.属性注入 2.构造器注入 3.set方式注入 1.属性注入: package com.ming.user.test; import com.google.inje ...