MySQL的一次优化记录 (IN子查询和索引优化)
这两天实习项目遇到一个网页加载巨慢的问题(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 子查询 效率慢 优化(转)
mysql in 子查询 效率慢 优化(转) 现在的CMS系统.博客系统.BBS等都喜欢使用标签tag作交叉链接,因此我也尝鲜用了下.但用了后发现我想查询某个tag的文章列表时速度很慢,达到5秒之久! ...
- MySQL多表查询之外键、表连接、子查询、索引
MySQL多表查询之外键.表连接.子查询.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复的,不允许为 ...
- MySQL数据库学习笔记(六)----MySQL多表查询之外键、表连接、子查询、索引
本章主要内容: 一.外键 二.表连接 三.子查询 四.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复 ...
- MySQL(18):Select- subquery子查询
1. Select- subquery子查询 子查询:是将一条查询语句嵌套在另一条查询语句之中. 2. 案例 需求:查询获得代课天数最多的那个老师的信息. 思路:先获得最多的代课天数是多少天,然后再判 ...
- MySQL数据库学习笔记----MySQL多表查询之外键、表连接、子查询、索引
本章主要内容: 一.外键 二.表连接 三.子查询 四.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复 ...
- mysql学习笔记-- 多表查询之外键、表连接、子查询、索引
本章主要内容: 一.外键 二.表连接 三.子查询 四.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复 ...
- MySQL在字段中使用select子查询
前几天看别人的代码中看到在字段中使用select子查询的方法,第一次见这种写法,然后研究了一下,记录下来 大概的形式是这样的: select a .*,(select b.another_field ...
- mysql 的delete from 和update子查询限制
最经做项目时发现的问题,好像在update时也有... 网上查到的资料如下: 1.使用mysql进行delete from操作时,若子查询的 FROM 字句和更新/删除对象使用同一张表,会出现错误. ...
- MySQL学习笔记(五)—— 子查询及联结
子查询: 子查询,即嵌套在其他查询中的查询.例如我们有这样几个表,顾客表,订单表,商品表,我们想知道有哪些客户买了商品A,那么我们就需要先查看哪些订单里包含了商品A,然后根据订单查出是哪些客户. my ...
随机推荐
- 【记录】Mybatis-Generator 数据层代码生成器,自动生成dao类,mapper,pojo类
Mybatis-Generator 工具来帮我们自动创建pojo类.mapper文件以及dao类并且会帮我们配置好它们的依赖关系. 官方文档地址:http://mybatis.org/generato ...
- 分支结构if 语句举例
- Codeforces 1185G2 Playlist for Polycarp (hard version) 背包,暴力
题意及思路:https://www.cnblogs.com/Als123/p/11061147.html 代码: #include <bits/stdc++.h> #define LL l ...
- 大数据基础环境--jdk1.8环境安装部署
1.环境说明 1.1.机器配置说明 本次集群环境为三台linux系统机器,具体信息如下: 主机名称 IP地址 操作系统 hadoop1 10.0.0.20 CentOS Linux release 7 ...
- Repeater的使用
1.页面代码 如果要分页,那么页面开头必须写(<%@ Register Src="~/Controls/Page.ascx" TagName="Page" ...
- enumerate()(Python)
>>> E=enumerate('spam') >>> E <enumerate object at 0x1021ceca8> >>> ...
- Anaconda/Conda创建环境时报错的解决方案
按照Conda网站上的提示安装完Conda之后,想要用conda create创建环境,一直报错: ERROR conda.core.link:_execute_actions(337): An er ...
- 负载均衡中间件(一)Nginx高性能负载均衡器
Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/PO3)代理服务器,并在一个BSD协议下发行,可以在UNIX.GNU/Linux.BSD.Mac OS X.Solaris,以 ...
- 【leetcode】980. Unique Paths III
题目如下: On a 2-dimensional grid, there are 4 types of squares: 1 represents the starting square. Ther ...
- GO 学习资源收集
golang图书,在线阅读Go轻松学https://www.golang123.com/book/16 Go示例学https://www.golang123.com/book/17 Go Web 编 ...