这两天实习项目遇到一个网页加载巨慢的问题(10多秒),然后定位到是一个MySQL查询特别慢的语句引起的:

SELECT *
FROM (
SELECT DISTINCT t.vc_date, t.c_bankno, t.vc_bankacco, t.vc_moneytype, t.en_totalbala
, t.en_usablebala, t1.vc_nameinbank, date_format(t.D_IMPORTTIME, '%Y-%m-%d %H:%i:%S') AS D_IMPORTTIME
, t.vc_fundcode, t.c_datamode, t.vc_taskid, t.id, t.vc_projectname
, t.vc_projectcode, t1.c_accotype
, (
SELECT IF(vc_occurtime IS NULL, DATE_FORMAT(vc_occurdate, '%Y-%m-%d'), DATE_FORMAT(CONCAT(vc_occurdate, vc_occurtime), '%Y-%m-%d %H:%i:%S')) AS tradeTime
FROM tbanktradedetail_view
WHERE vc_bankacco = t.vc_bankacco
) AS d_tradetime, t3.vc_entry_caption AS C_ACCOTYPE_STR, t5.vc_entry_caption AS VC_BANKNAME, t4.vc_entry_caption AS VC_MONEYTYPE_STR
FROM tbankaccobala t
INNER JOIN tbankaccoinfo t1 ON t.vc_bankacco = t1.vc_bankacco
INNER JOIN (
SELECT vc_entry_value, vc_entry_caption
FROM ot_dic_tdictionaryentry
WHERE vc_entry_no = '5087'
) t3
ON t1.c_accotype = t3.vc_entry_value
INNER JOIN (
SELECT vc_entry_value, vc_entry_caption
FROM ot_dic_tdictionaryentry
WHERE vc_entry_no = '1004'
) t4
ON t1.VC_MONEYTYPE = t4.vc_entry_value
INNER JOIN (
SELECT vc_entry_value, vc_entry_caption
FROM ot_dic_tdictionaryentry
WHERE vc_entry_no = '1014'
) t5
ON t.c_bankno = t5.vc_entry_value
WHERE 1 = 1
AND t.id IN (
-- this query will take 4.6s:
-- SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY d_importtime DESC), ',', 1)
-- FROM tbankaccobala
-- GROUP BY vc_bankacco
-- but the following query only takes 1.1s:
SELECT hhhh from(
SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY d_importtime DESC), ',', 1) as hhhh
FROM tbankaccobala
GROUP BY vc_bankacco
) as sbstr
-- 对IN的子查询做二次查询
)
) t
WHERE 1 = 1
ORDER BY t.D_IMPORTTIME DESC

抽出查询慢关键部分:

SELECT *
FROM (
SELECT DISTINCT t.vc_date, t.c_bankno, t.vc_bankacco, t.vc_moneytype, t.en_totalbala
-- 此处省略选择多个列语句
FROM tbankaccobala t
-- 此处省略多张表连表查询语句
WHERE 1 = 1
AND t.id IN (
-- 这个查询需要3s:
SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY d_importtime DESC), ',', 1)
FROM tbankaccobala
GROUP BY vc_bankacco
)
) t

这个语句导致前端页面10多秒才有响应(但MySQL执行显示要4.6秒,phpMyAdmin也是10秒左右响应,为何?)

IN子查询语句优化

把IN语句里面的内容改成下面这样,只在外层再加一个select,就把3s的查询缩短为0.006s:

            SELECT hhhh from(
SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY d_importtime DESC), ',', 1) as hhhh
FROM tbankaccobala
GROUP BY vc_bankacco
) as sbstr
-- 对IN的子查询做二次select,或者把IN改为JOIN都可以解决速度奇慢的问题

原语句空行处省略了一系列的其他表和 INNER JOIN 语句。一开始怀疑是多表的JOIN操作导致速度变慢,但删去JOIN变成上面这段注释掉的语句之后,速度依然非常慢,显示要3s,于是猜测 IN 才是导致速度变慢的主要因素,改后只要0.006s,啧…

EXPLAIN 未优化的语句:

(相关子查询是使用外部查询中的值的子查询)

EXPLAIN 优化的语句:

我的理解:优化前,子查询是相关子查询,对于外部产生的每个值,都要执行一次子查询;优化后,子查询不再是相关子查询,只需要执行一次子查询并缓存中间结果,外部查到的每个值去缓存的中间结果里比对一下就行了。

(有人说是能不能用索引的原因——这么说应该是不对的)

完整查询的后端响应速度对比:
前:

后:

索引优化

对于这么小的数据规模,时间还是太长了… 看前面explain执行计划的截图,嗯,没有索引…
给t1的vc_bankacco加上索引之后


解释执行计划:

查询和网页响应用时大幅缩短:

再看sql里还有三个join:

用的都是ot_dic_tdictionaryentry这张表的t4.vc_entry_value字段,那么试着给这个字段也加上索引吧,然后用时如下:

是的,时间反而变长了

explain执行计划:

所以变慢原因是:

没加vc_entry_value的索引时,会先用vc_entry_no选出一个数量很小的表,再和t1做join,

而加了vc_entry_value的索引之后,MySQL就把这个索引用了起来,join语句被优化为先FirstMatch(ot_dic_tdictionaryentry),这产生了一个1713*1713=2934369行的中间结果(笛卡尔乘积),然后才使用vc_entry_no进行where过滤。

所以索引不能乱加啊,加错了反而会导致性能下降!这个示例里的查询要加索引只能在vc_entry_no上索引,而不能在vc_entry_value上!

这个示例中主要提升是IN子查询语句的优化。在使用索引的情况下,对IN子查询做优化前后的查询时间分别是3.1s和0.16s

深入理解MySql子查询IN的执行和优化
多个单列索引和联合索引的区别详解

MySQL的一次优化记录 (IN子查询和索引优化)的更多相关文章

  1. mysql in 子查询 效率慢 优化(转)

    mysql in 子查询 效率慢 优化(转) 现在的CMS系统.博客系统.BBS等都喜欢使用标签tag作交叉链接,因此我也尝鲜用了下.但用了后发现我想查询某个tag的文章列表时速度很慢,达到5秒之久! ...

  2. MySQL多表查询之外键、表连接、子查询、索引

    MySQL多表查询之外键.表连接.子查询.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复的,不允许为 ...

  3. MySQL数据库学习笔记(六)----MySQL多表查询之外键、表连接、子查询、索引

    本章主要内容: 一.外键 二.表连接 三.子查询 四.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复 ...

  4. MySQL(18):Select- subquery子查询

    1. Select- subquery子查询 子查询:是将一条查询语句嵌套在另一条查询语句之中. 2. 案例 需求:查询获得代课天数最多的那个老师的信息. 思路:先获得最多的代课天数是多少天,然后再判 ...

  5. MySQL数据库学习笔记----MySQL多表查询之外键、表连接、子查询、索引

    本章主要内容: 一.外键 二.表连接 三.子查询 四.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复 ...

  6. mysql学习笔记-- 多表查询之外键、表连接、子查询、索引

    本章主要内容: 一.外键 二.表连接 三.子查询 四.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复 ...

  7. MySQL在字段中使用select子查询

    前几天看别人的代码中看到在字段中使用select子查询的方法,第一次见这种写法,然后研究了一下,记录下来 大概的形式是这样的: select a .*,(select b.another_field ...

  8. mysql 的delete from 和update子查询限制

    最经做项目时发现的问题,好像在update时也有... 网上查到的资料如下: 1.使用mysql进行delete from操作时,若子查询的 FROM 字句和更新/删除对象使用同一张表,会出现错误. ...

  9. MySQL学习笔记(五)—— 子查询及联结

    子查询: 子查询,即嵌套在其他查询中的查询.例如我们有这样几个表,顾客表,订单表,商品表,我们想知道有哪些客户买了商品A,那么我们就需要先查看哪些订单里包含了商品A,然后根据订单查出是哪些客户. my ...

随机推荐

  1. Logback配置,error和普通日志分离

    <?xml version="1.0" encoding="utf-8"?> <configuration> <springPro ...

  2. Spring 2.5配置文件详解(转)

    http://book.51cto.com/art/201004/193743.htm 6.2.3  Spring 2.5配置文件详解 Spring配置文件是用于指导Spring工厂进行Bean生产. ...

  3. 微信浏览器 video - android适配

    阶段一: 直接裸用 video 标签, 安卓 - 会重新弹一个播放层, 和之前video的父盒子错位, 要多丑有多丑, 体验要多烂有多烂. 阶段二: video添加以下属性, 安卓可实现内联播放, 但 ...

  4. jquery实现放大镜简单方法

    网上有许多放大镜的jquery的插件,但是用着不是那么得心应手,现在一页代码实现一个放大镜功能,如果需要附加的功能可以手动修改,原理都在注释里 1 2 3 4 5 6 7 8 9 10 11 12 1 ...

  5. Python的return语句中使用条件判断

    if end1 <= val <= end2 or end2 <= val <= end1: return True else: return False 等于 return ...

  6. 【leetcode】908. Smallest Range I

    题目如下: 解题思路:简单的不能再简单的题目了,对于任意一个A[i]来说,其可能的最小的最大值是A[i]-K,最大的最小值是A[i]+K.遍历数组,求出所有元素中最大的最小值和最小的最大值,两者之差( ...

  7. django 框架下的路由分发

  8. sql server 基础语法4 实践练习+子查询

    drop table class create table class ( classId ) primary key not null, cName ) ) insert into class ', ...

  9. Cisco基础(六):配置目前网络环境、项目阶段练习

    一.配置目前网络环境 目标: 一家新创建的IT公司,公司位于北京有80多台服务器 目前网络环境使用技术,通过端口映射技术将web服务器发布给Internet: 三层交换:汇聚接入层交换机 默认路由:实 ...

  10. paper 141:some paper with ComputerCV、MachineLearning[转]

    copy from:http://blog.csdn.net/zouxy09/article/details/8550952 一.特征提取Feature Extraction: ·         S ...