1.需要优化的sql

最近做一个基于.net mvc和MySQL的仓储系统的优化工作,遇到了一个执行特别慢的SQL语句,经过一番折腾,终于搞定啦,分享一下过程。问题就是下面这个家伙:

create or replace view view_task_meter_info
as
select t1.TASK_ID,t1.task_no,t1.BINDBOX_BARCODE as box_barcode,t1.EQUIP_BAR_CODE,t1.METER_STATUS,t1.ENTITY_TYPE as RSLT_CODE,
-- 设备类别
(SELECT name from data_dictionary_info t01 where t01.domain ='设备类别' and t01.code = t3.EQUIP_CATEG) as T_Equip_categ,
-- 类别
(select name from data_dictionary_info t09 where t09.domain = '类型' and t09.code = t3.TYPE_CODE) as
T_TYPE_CODE,
-- 类型
(select name from data_dictionary_info t09 where t09.domain = '类别' and t09.code = t3.RATED_CURRENT) as T_SORT_CODE,
t3.EQUIP_CATEG,t3.TYPE_CODE,t3.SORT_CODE
from
data_task_asset t1 left join data_meter_info t3 on t1.EQUIP_BAR_CODE=t3.BAR_CODE
union all
select t1.CHK_TASK_ID as task_id,t1.TASK_NO,t1.BOX_BARCODE,t1.BAR_CODE as Equip_bar_code,'00' as METER_STATUS,t1.RSLT_CODE,
-- 设备类别
(SELECT name from data_dictionary_info t01 where t01.domain ='设备类别' and t01.code = t3.EQUIP_CATEG) as T_Equip_categ,
-- 类别
(select name from data_dictionary_info t09 where t09.domain = '类型' and t09.code = t3.TYPE_CODE) as
T_TYPE_CODE,
-- 类型
(select name from data_dictionary_info t09 where t09.domain = '类别' and t09.code = t3.RATED_CURRENT) as T_SORT_CODE,
t3.EQUIP_CATEG,t3.TYPE_CODE,t3.SORT_CODE
from data_check_asset_info t1
LEFT JOIN data_meter_info t3 on t1.BAR_CODE = t3.BAR_CODE

解释一下业务:

仓储系统业务分为三块:出入库、盘点。data_task_asset是出入库任务资产明细,data_check_asset_info是盘点任务明细,data_meter_info是资产档案表。data_task_asset和data_check_asset_info表都使用资产条码(EQUIP_BAR_CODE/BAR_CODE)和资产档案表关联(BAR_CODE是档案表主键)。这个视图的业务意义就是展示出入库、盘点任务的资产明细(包括档案信息),同时需要把档案信息里面的大量代码字段翻译成文字信息。上面视图中只列出3个字段作为示例,实际上需要翻译的字段有十几个。

这个视图刚开始没有感觉慢,但是有一天测试做了一个7万多条明细的盘点任务后,每次查询一个任务的明细都要等上十几到几十秒,是在难以忍受。开工吧,先诊断一下。

2.查看执行情况

下面是这个视图在查询那个7万多条明细的盘点任务的执行时间,太可怕了,三十多秒。

看看执行计划

额滴神呀,这可肯定不行呀,每个字段的翻译都要查询字典表的1207条记录,一个记录需要翻译10次,7万条记录,需要查询字典表70万次,每次搜索记录1000多条,这个当然不行啦。

3.在字典表上加索引!!!

根据查询字典表的sql语句,我们在domain和code上加联合索引

SELECT name from data_dictionary_info t01 where t01.domain ='设备类别' and t01.code = t3.EQUIP_CATEG

来看现在的执行情况

执行时间:

执行时间一下子降到了两三秒,效果显著呀!

执行计划:

看看执行计划你也许就不吃惊啦,建立索引后每次查询字典表,只搜索一条记录。

4.再加两个索引

既然索引这么厉害,那就继续加索引呗,可以看到视图在查询出入库和盘点任务明细表时,也是全表查询。我们加个索引看看效果如何,我分别在data_task_asset的task_id和task_no、data_check_asset_info的chk_task_id和task_no上添加了联合索引。下面看看执行情况:

执行时间:

执行时间好像比刚才还长了一点,这就不合心意啦。

执行计划:

从执行计划来看,查询根本就没有用到索引,why?

5.mysql视图算法及不使用索引的情况

普及一些百度知识:

当用户创建视图时,mysql默认使用一种undefine的处理算法,就是会自动在合并和临时表内进行选择

  • 对于MERGE,会将引用视图的语句的文本与视图定义合并起来,使得视图定义的某一部分取代语句的对应部分。

  • 对于TEMPTABLE,视图的结果将被置于临时表中,然后使用它执行语句。

  • 对于UNDEFINED,MySQL将选择所要使用的算法。如果可能,它倾向于MERGE而不是TEMPTABLE,这是因为MERGE通常更有效,而且如果使用了临时表,视图是不可更新。

对于使用MERGE算法处理的视图,可以使用索引。但是,对于使用临时表算法处理的视图,不能在其基表上利用索引提供的优点。MERGE算法要求视图中的行和基表中的行具有一对一的关系。如果不具有该关系。必须使用临时表取而代之。如果视图包含下述结构中的任何一种,将失去一对一的关系:

  • 聚合函数(SUM(),MIN(),MAX(),COUNT()等)
  • DISTINCT
  • GROUP BY
  • HAVING
  • UNION或UNION ALL

通常的不使用索引的查询

  • 如果MySQL估计使用索引比全表扫描更慢,则不使用索引。例如,如果列key均匀分布在1和100之间,下面的查询使用索引就不是很好:select * from table_name where key>1 and key<90;

  • 如果使用MEMORY/HEAP表,并且where条件中不使用“=”进行索引列,那么不会用到索引,head表只有在“=”的条件下才会使用索引

  • 用or分隔开的条件,如果or前的条件中的列有索引,而后面的列没有索引,那么涉及到的索引都不会被用到,例如:select * from table_name where key1='a' or key2='b';如果在key1上有索引而在key2上没有索引,则该查询也不会走索引

  • 复合索引,如果索引列不是复合索引的第一部分,则不使用索引(即不符合最左前缀),例如,复合索引为(key1,key2),则查询select * from table_name where key2='b';将不会使用索引

  • 如果like是以‘%’开始的,则该列上的索引不会被使用。例如select * from table_name where key1 like '%a';该查询即使key1上存在索引,也不会被使用

  • 如果列为字符串,则where条件中必须将字符常量值加引号,否则即使该列上存在索引,也不会被使用。例如,select * from table_name where key1=1;如果key1列保存的是字符串,即使key1上有索引,也不会被使用。

6.干掉union all

为了证明确实是union all影响了索引的使用,我们去掉视图中的union all,让视图只负责查询盘点任务的明细及档案信息,看看效果如何。

-执行时间:

时间又比刚才短了一秒钟,不错,不错。

-执行计划

从执行计划可以看出,这次用到了明细表的索引。

7.存储过程使用

我们看到取到union all之后,明细表的索引在查询中被使用。尽管我们从查询的时间上感觉明细表使用索引和不使用索引没有太大差别。但这其实只是数据太少反映不出问题,随着明细表数据的增多,有索引时每个任务搜索的记录数只与明细数量有关;而无索引时,每个任务明细查询是全表搜索。所以,union all必须去掉。

那么问题来啦,程序结构已经基本定型,单个视图必须使用union all。这里有两种方案:

  • 分开为两个视图,出入库明细一个视图,盘点明细一个视图,在程序中控制使用不同视图;
  • 使用存储过程,在存储过程中判断查询出入库明细还是盘点明细。

在Entity Framework中使用存储过程还没尝试过,就用存储过程啦:

mysql存储过程代码:

CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_task_meter_info`(IN `taskId` DECIMAL(16,0), IN `taskNo` VARCHAR(32), IN `ioFlag` VARCHAR(8))
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
if ioFlag = '盘点' then
-- 返回盘点明细
select * from ...
where ...
else
-- 返回出入库明细
select * from ...
where ...
end if;
END

Entity Framework调用存储过程(看上去也挺方便的):

var dataListProc =
DbContext.Database.SqlQuery<view_task_meter_info>(
string.Format("CALL `sp_task_meter_info`({0}, '{1}','{2}')",searchModel.task_id,searchModel.task_no,searchModel.task_type)).ToList();

8.疑问

  • 总感觉自己的翻译代码字段有点太费时,不知各位园友是怎么处理这种问题的。

好了,就到这里啦。这篇博客其实早该发出来的,因为一些耽搁,今天总算赶出来啦。我感觉每次要写一篇博客前,总感觉有好多东西要说,可是很多时候赶紧文章都写不流畅啦。动不动就想分条陈述,动不动就想来个问题原因,解决方案。而且一旦不能集中时间写完,再回首已是食之无味。成了个整天宥在自己项目、任务圈里的程序猿啦。这不行!

一个MySQL视图的优化过程的更多相关文章

  1. 【夯实Mysql基础】记一次mysql语句的优化过程

    1. [事件起因] 今天在做项目的时候,发现提供给客户端的接口时间很慢,达到了2秒多,我第一时间,抓了接口,看了运行的sql,发现就是 2个sql慢,分别占了1秒多. 一个sql是 链接了5个表同时使 ...

  2. 【夯实Mysql基础】记一次mysql语句的优化过程!

      1. [事件起因] 今天在做项目的时候,发现提供给客户端的接口时间很慢,达到了2秒多,我第一时间,抓了接口,看了运行的sql,发现就是 2个sql慢,分别占了1秒多. 一个sql是 链接了5个表同 ...

  3. MySQL 数据库性能优化之缓存参数优化

    在平时被问及最多的问题就是关于 MySQL 数据库性能优化方面的问题,所以最近打算写一个MySQL数据库性能优化方面的系列文章,希望对初中级 MySQL DBA 以及其他对 MySQL 性能优化感兴趣 ...

  4. MySQL查询语句执行过程及性能优化(JOIN/ORDER BY)-图

    http://blog.csdn.net/iefreer/article/details/12622097 MySQL查询语句执行过程及性能优化-查询过程及优化方法(JOIN/ORDER BY) 标签 ...

  5. MySQL查询语句执行过程及性能优化-查询过程及优化方法(JOIN/ORDER BY)

    在上一篇文章MySQL查询语句执行过程及性能优化-基本概念和EXPLAIN语句简介中介绍了EXPLAIN语句,并举了一个慢查询例子:

  6. 一次MySQL两千万数据大表的优化过程,三种解决方案

    问题概述 使用阿里云rds for MySQL数据库(就是MySQL5.6版本),有个用户上网记录表6个月的数据量近2000万,保留最近一年的数据量达到4000万,查询速度极慢,日常卡死.严重影响业务 ...

  7. MySQL查询语句执行过程及性能优化-基本概念和EXPLAIN语句简介

    网站或服务的性能关键点很大程度在于数据库的设计(假设你选择了合适的语言开发框架)以及如何查询数据上. 我们知道MySQL的性能优化方法,一般有建立索引.规避复杂联合查询.设置冗余字段.建立中间表.查询 ...

  8. 一个扩展搜索API的优化过程

    概述 API 是一个服务的门面,就像衣装是人的形象一样. 优雅的 API 设计,能让业务方使用起来倍儿爽,提升开发效率,降低维护成本:糟糕的 API 设计,则让业务方遭心,陷入混沌. 本文将展示一个扩 ...

  9. Mysql原理与优化

    原文:https://mp.weixin.qq.com/s__biz=MzI4NTA1MDEwNg==&mid=2650763421&idx=1&sn=2515421f09c1 ...

随机推荐

  1. 查询结果集转换成HTML存储过程

    工作中经常需要用SQLServer发送报警或者业务报表邮件,每次现拼串也不是办法,故写了一个TableResult to HTML的存储过程 USE master; GO -- Description ...

  2. Java编程题: 写一个Singleton出来

    Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 一般Singleton模式通常有几种种形式: 第一种形式:  定义一个类,它的构造函数为private的, ...

  3. [Swift]LeetCode523. 连续的子数组和 | Continuous Subarray Sum

    Given a list of non-negative numbers and a target integer k, write a function to check if the array ...

  4. RabbitQM使用笔记

    一:安装  and  卸载 curl -s https://packagecloud.io/install/repositories/rabbitmq/rabbitmq-server/script.r ...

  5. 【Spark篇】---SparkSQL on Hive的配置和使用

    一.前述 Spark on Hive: Hive只作为储存角色,Spark负责sql解析优化,执行. 二.具体配置 1.在Spark客户端配置Hive On Spark 在Spark客户端安装包下sp ...

  6. DOM事件第二弹(UIEvent事件)

    此文章主要总结UIEvent相关的事件,如有不对的地方,欢迎指正. 一.uitls.js(绑定事件公共类) var fixs = { 'focusin': { standard: 'focus', i ...

  7. DOM事件第一弹

    近期温习了部分w3c上关于DOM事件的规范,发现以前有些模糊的概念更加清晰,以及受到罗胖(罗辑思维)的影响,很是想分享自己的了解的东西,希望大家给予指正或补充. 一.事件类型 参数 事件接口 初始化方 ...

  8. sql server 性能调优之 死锁排查

    一.概述 记得以前客户在使用软件时,有偶发出现死锁问题,因为发生的时间不确定,不好做问题的重现,当时解决问题有点棘手了.现总结下查看死锁的常用二种方式. 1.1 第一种是图形化监听: sqlserve ...

  9. Linux设备驱动之IIO子系统——IIO框架及IIO数据结构

    由于需要对ADC进行驱动设计,因此学习了一下Linux驱动的IIO子系统.本文翻译自<Linux Device Drivers Development >--John Madieu,本人水 ...

  10. Linux服务器时间相关命令记录

    前言 以往安装服务器时间都是正常,但是最近服务器的时间经常出现问题,所以在安装配置完成服务器之后需要对服务器的时间进行测试,如果服务器时间异常,那么当程序去取系统时间的时候就会出现问题. 时间相关命令 ...