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 ...
随机推荐
- Logback配置,error和普通日志分离
<?xml version="1.0" encoding="utf-8"?> <configuration> <springPro ...
- Spring 2.5配置文件详解(转)
http://book.51cto.com/art/201004/193743.htm 6.2.3 Spring 2.5配置文件详解 Spring配置文件是用于指导Spring工厂进行Bean生产. ...
- 微信浏览器 video - android适配
阶段一: 直接裸用 video 标签, 安卓 - 会重新弹一个播放层, 和之前video的父盒子错位, 要多丑有多丑, 体验要多烂有多烂. 阶段二: video添加以下属性, 安卓可实现内联播放, 但 ...
- jquery实现放大镜简单方法
网上有许多放大镜的jquery的插件,但是用着不是那么得心应手,现在一页代码实现一个放大镜功能,如果需要附加的功能可以手动修改,原理都在注释里 1 2 3 4 5 6 7 8 9 10 11 12 1 ...
- Python的return语句中使用条件判断
if end1 <= val <= end2 or end2 <= val <= end1: return True else: return False 等于 return ...
- 【leetcode】908. Smallest Range I
题目如下: 解题思路:简单的不能再简单的题目了,对于任意一个A[i]来说,其可能的最小的最大值是A[i]-K,最大的最小值是A[i]+K.遍历数组,求出所有元素中最大的最小值和最小的最大值,两者之差( ...
- django 框架下的路由分发
- sql server 基础语法4 实践练习+子查询
drop table class create table class ( classId ) primary key not null, cName ) ) insert into class ', ...
- Cisco基础(六):配置目前网络环境、项目阶段练习
一.配置目前网络环境 目标: 一家新创建的IT公司,公司位于北京有80多台服务器 目前网络环境使用技术,通过端口映射技术将web服务器发布给Internet: 三层交换:汇聚接入层交换机 默认路由:实 ...
- paper 141:some paper with ComputerCV、MachineLearning[转]
copy from:http://blog.csdn.net/zouxy09/article/details/8550952 一.特征提取Feature Extraction: · S ...