MySQL优化一例
DELIMITER $$ USE `xxx`$$ DROP FUNCTION IF EXISTS `F_getBuluDates`$$ CREATE DEFINER=`root`@`localhost` FUNCTION `F_getBuluDates`(`PuserId` INT(11)) RETURNS VARCHAR(3000) CHARSET gbk
BEGIN
DECLARE buluDate,buluDateTime,LiWaibuluDates,nowdate,bldate VARCHAR(3000) DEFAULT "";
DECLARE rtMsg,RuserName,maxRiZhiDate,btjrqs,blyqrq,tmpdate,yxblsj VARCHAR(255) DEFAULT "";
DECLARE Ra51count INT(30);
DECLARE RbeginDate,RendDate,RtmpDate VARCHAR(2550);
DECLARE u,gqcount,a21Count INT(30) DEFAULT 0;
DECLARE allReadRows,readRows,mySYL,blCount,recordCount,btjryCount,btjrqCount,yxblts,i INT(11) DEFAULT 0;
SELECT a5204,a5205 INTO yxblts,yxblsj FROM a52 WHERE a5218="xxxxxxx" LIMIT 0,1; SELECT username INTO RuserName FROM USER WHERE id=PuserId; SELECT COUNT(id) INTO btjryCount FROM a52 WHERE a5214=PuserId AND a5218='xxxxx';
IF btjryCount=0 THEN SELECT GROUP_CONCAT(a5215) INTO btjrqs FROM a52 WHERE a5218='btjrq';
SET tmpdate=CURDATE();
SET nowdate=DATE_SUB(CURDATE(),INTERVAL yxblts DAY); REPEAT
SELECT MAX(a2104) INTO maxRiZhiDate FROM a21 WHERE issubmit='y' AND a2104<tmpdate AND FIND_IN_SET(a2104,bldate)=0;
SET tmpdate=maxRiZhiDate;
IF tmpdate>=nowdate THEN
IF F_isWorkDay(maxRiZhiDate)=1 OR ISNULL(maxRiZhiDate) THEN
IF NOT ISNULL(maxRiZhiDate) AND LENGTH(maxRiZhiDate)>0 THEN
IF FIND_IN_SET(maxRiZhiDate,btjrqs)=0 THEN
SELECT COUNT(id) INTO recordCount FROM a21 WHERE a2104=maxRiZhiDate AND creatorid=PuserId;
IF recordCount=0 THEN
IF DAYOFWEEK(maxRiZhiDate)=6 THEN
SET buluDateTime=CONCAT(FROM_DAYS(TO_DAYS(maxRiZhiDate)+2+yxblts)," ",yxblsj);
ELSEIF DAYOFWEEK(maxRiZhiDate)=7 THEN
SET buluDateTime=CONCAT(FROM_DAYS(TO_DAYS(maxRiZhiDate)+1+yxblts)," ",yxblsj);
ELSE
SET buluDateTime=CONCAT(FROM_DAYS(TO_DAYS(maxRiZhiDate)+yxblts)," ",yxblsj);
END IF; IF(CURRENT_TIMESTAMP()<buluDateTime) THEN
SET buluDate=maxRiZhiDate;
END IF;
END IF;
END IF;
END IF; END IF;
IF CHAR_LENGTH(buluDate)>0 THEN
IF CHAR_LENGTH(bldate)>0 THEN
SET bldate=CONCAT(buluDate,",",bldate);
ELSE
SET bldate=buluDate;
END IF;
END IF;
END IF; SET buluDate='';
UNTIL tmpdate<=nowdate
END REPEAT; SELECT COUNT(id) INTO gqcount FROM a52 WHERE a5216=CURDATE() AND a5218='xxxxxx';
IF gqcount>0 THEN
SELECT a5215 INTO blyqrq FROM a52 WHERE a5216=CURDATE() AND a5218='xxxxxx';
SELECT COUNT(id) INTO a21Count FROM a21 WHERE a2104=blyqrq AND creatorid=PuserId;
IF a21Count=0 THEN
IF FIND_IN_SET(blyqrq,bldate)=0 THEN
IF CHAR_LENGTH(bldate)>0 THEN
SET bldate=CONCAT(blyqrq,",",bldate);
ELSE
SET bldate=blyqrq;
END IF;
END IF;
END IF;
END IF; IF ISNULL(btjrqs) THEN
SET btjrqs="";
END IF; SELECT GROUP_CONCAT(a5108) INTO LiWaibuluDates FROM a51 WHERE a5110='y' AND F_ifInSet(a5108,btjrqs)=0 AND a5106 LIKE RuserName AND a5112='否' AND CONCAT(a5108," ",a5113)<CURRENT_TIMESTAMP() AND CURRENT_TIMESTAMP()<CONCAT(a5114," ",a5113);
IF NOT ISNULL(LiWaibuluDates) AND LENGTH(TRIM(LiWaibuluDates))>0 THEN
IF CHAR_LENGTH(bldate)>0 THEN
SET bldate=CONCAT(LiWaibuluDates,",",bldate);
ELSE
SET bldate=LiWaibuluDates;
END IF;
END IF; SELECT COUNT(id) INTO RA51count FROM a51 WHERE a5110='n' AND F_getid1value2(a5105,1)=PuserId;
IF RA51count>0 THEN
SET u=0;
WHILE u< RA51count DO
SELECT a5108,a5109 INTO RbeginDate,RendDate FROM a51 WHERE a5110='n' AND F_getid1value2(a5105,1)=PuserId ORDER BY id LIMIT u,1;
SET RtmpDate=RbeginDate;
WHILE RtmpDate<=RendDate DO IF FIND_IN_SET(RtmpDate,bldate)>0 THEN
SET bldate=F_removeFromSet(RtmpDate,bldate);
END IF;
SET RtmpDate=DATE_ADD(RtmpDate, INTERVAL 1 DAY);
END WHILE;
SET u=u+1;
END WHILE;
END IF; END IF; RETURN bldate; END$$ DELIMITER ;
在老系统中该函数调用一次需要话20多秒到30秒左右。
拿到sql之后,首先要确定思路。不能着急这下手。
1. 首先查看各个表的数据量:
select count(*) from xxx;
发现只有 a21 的数据量达到了十几万,其他表数据量都比较小。所以重点是 a21表,仔细阅读了一遍函数的定义,发现涉及 到 a21 的有一处循环:
REPEAT
SELECT MAX(a2104) INTO maxRiZhiDate FROM a21 WHERE issubmit='y' AND a2104<tmpdate AND FIND_IN_SET(a2104,bldate)=0;
很显然使用了 find_in_set 函数,所以改语句无法使用索引。所以想要简单的通过增加索引来解决问题,应该是行不通的。
这样就定位到了函数运行慢的问题。结合业务理解改函数的含义。
为了使用索引,我们需要去掉 find_in_set 函数,理解了业务和原函数的功能之后,对改函数的 repeat 循环部分进行了重写:
DELIMITER $$ USE `oa`$$ DROP FUNCTION IF EXISTS `F_getBuluDates_inner`$$ CREATE DEFINER=`root`@`localhost` FUNCTION `F_getBuluDates_inner`(`PuserId` INT(11), tmpdate VARCHAR(32), nowdate VARCHAR(32)) RETURNS VARCHAR(3000) CHARSET gbk
BEGIN
DECLARE buluDate,buluDateTime,LiWaibuluDates,bldate VARCHAR(3000) DEFAULT "";
DECLARE rtMsg,RuserName,maxRiZhiDate,btjrqs,blyqrq,yxblsj VARCHAR(255) DEFAULT "";
DECLARE Ra51count INT(30);
DECLARE RbeginDate,RendDate,RtmpDate VARCHAR(2550);
DECLARE u,gqcount,a21Count INT(30) DEFAULT 0;
DECLARE allReadRows,readRows,mySYL,blCount,recordCount,btjryCount,btjrqCount,yxblts,i INT(11) DEFAULT 0;
DECLARE no_more_data INT DEFAULT 0; DECLARE my_cursor CURSOR FOR SELECT DISTINCT a2104 FROM a21 WHERE issubmit='y' AND a2104 < tmpdate AND a2104 >= nowdate ORDER BY a2104 DESC;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_data = 1; SELECT a5204,a5205 INTO yxblts,yxblsj FROM a52 WHERE a5218="gzrz" LIMIT 0,1; SELECT username INTO RuserName FROM USER WHERE id=PuserId; SELECT COUNT(id) INTO btjryCount FROM a52 WHERE a5214=PuserId AND a5218='xxxxx';
IF btjryCount=0 THEN SELECT GROUP_CONCAT(a5215) INTO btjrqs FROM a52 WHERE a5218='xxx'; OPEN my_cursor;
FETCH my_cursor INTO maxRiZhiDate; REPEAT
IF F_isWorkDay(maxRiZhiDate)=1 OR ISNULL(maxRiZhiDate) THEN
IF NOT ISNULL(maxRiZhiDate) AND LENGTH(maxRiZhiDate)>0 THEN
IF FIND_IN_SET(maxRiZhiDate,btjrqs)=0 THEN
SELECT COUNT(id) INTO recordCount FROM a21 WHERE a2104=maxRiZhiDate AND creatorid=PuserId;
IF recordCount=0 THEN
IF DAYOFWEEK(maxRiZhiDate)=6 THEN
SET buluDateTime=CONCAT(FROM_DAYS(TO_DAYS(maxRiZhiDate)+2+yxblts)," ",yxblsj);
ELSEIF DAYOFWEEK(maxRiZhiDate)=7 THEN
SET buluDateTime=CONCAT(FROM_DAYS(TO_DAYS(maxRiZhiDate)+1+yxblts)," ",yxblsj);
ELSE
SET buluDateTime=CONCAT(FROM_DAYS(TO_DAYS(maxRiZhiDate)+yxblts)," ",yxblsj);
END IF; IF(CURRENT_TIMESTAMP()<buluDateTime) THEN
SET buluDate=maxRiZhiDate;
END IF;
END IF;
END IF;
END IF; END IF;
IF CHAR_LENGTH(buluDate)>0 THEN
IF CHAR_LENGTH(bldate)>0 THEN
SET bldate=CONCAT(buluDate,",",bldate);
ELSE
SET bldate=buluDate;
END IF;
END IF; FETCH my_cursor INTO maxRiZhiDate;
SET buluDate='';
UNTIL no_more_data = 1
END REPEAT; SELECT COUNT(id) INTO gqcount FROM a52 WHERE a5216=CURDATE() AND a5218='xxx';
IF gqcount>0 THEN
SELECT a5215 INTO blyqrq FROM a52 WHERE a5216=CURDATE() AND a5218='xxxxxxxxxx';
SELECT COUNT(id) INTO a21Count FROM a21 WHERE a2104=blyqrq AND creatorid=PuserId;
IF a21Count=0 THEN
IF FIND_IN_SET(blyqrq,bldate)=0 THEN
IF CHAR_LENGTH(bldate)>0 THEN
SET bldate=CONCAT(blyqrq,",",bldate);
ELSE
SET bldate=blyqrq;
END IF;
END IF;
END IF;
END IF; IF ISNULL(btjrqs) THEN
SET btjrqs="";
END IF; SELECT GROUP_CONCAT(a5108) INTO LiWaibuluDates FROM a51 WHERE a5110='y' AND F_ifInSet(a5108,btjrqs)=0 AND a5106 LIKE RuserName AND a5112='否' AND CONCAT(a5108," ",a5113)<CURRENT_TIMESTAMP() AND CURRENT_TIMESTAMP()<CONCAT(a5114," ",a5113);
IF NOT ISNULL(LiWaibuluDates) AND LENGTH(TRIM(LiWaibuluDates))>0 THEN
IF CHAR_LENGTH(bldate)>0 THEN
SET bldate=CONCAT(LiWaibuluDates,",",bldate);
ELSE
SET bldate=LiWaibuluDates;
END IF;
END IF; SELECT COUNT(id) INTO RA51count FROM a51 WHERE a5110='n' AND F_getid1value2(a5105,1)=PuserId;
IF RA51count>0 THEN
SET u=0;
WHILE u< RA51count DO
SELECT a5108,a5109 INTO RbeginDate,RendDate FROM a51 WHERE a5110='n' AND F_getid1value2(a5105,1)=PuserId ORDER BY id LIMIT u,1;
SET RtmpDate=RbeginDate;
WHILE RtmpDate<=RendDate DO IF FIND_IN_SET(RtmpDate,bldate)>0 THEN
SET bldate=F_removeFromSet(RtmpDate,bldate);
END IF;
SET RtmpDate=DATE_ADD(RtmpDate, INTERVAL 1 DAY);
END WHILE;
SET u=u+1;
END WHILE;
END IF; END IF; RETURN bldate; END$$ DELIMITER ;
然后为了让新的 sql 使用索引:
DECLARE my_cursor CURSOR FOR
SELECT DISTINCT a2104 FROM a21 WHERE issubmit='y' AND a2104 < tmpdate AND a2104 >= nowdate ORDER BY a2104 DESC;
新增了索引:
add index issubmit_a2104(issubmit,a2104);
最终的提供给外部的调用函数:
DELIMITER $$
CREATE FUNCTION F_getBuluDates_new(`PuserId` INT(11)) RETURNS VARCHAR(3000) CHARSET gbk
BEGIN
DECLARE bldate VARCHAR(3000) DEFAULT "";
DECLARE tmpdate,nowdate VARCHAR(32) DEFAULT "";
DECLARE yxblts INT;
SELECT a5204 INTO yxblts FROM a52 WHERE a5218="gzrz" LIMIT 0,1;
SET tmpdate = CURDATE();
SET nowdate = DATE_SUB(tmpdate,INTERVAL yxblts DAY);
SET bldate = F_getBuluDates_inner(PuserId,tmpdate,nowdate);
RETURN bldate;
END
$$
测试效果:
mysql> select F_getBuluDates_new(10687);
+---------------------------------------------------------------------------------------------------------------+
| F_getBuluDates_new(10687) |
+---------------------------------------------------------------------------------------------------------------+
| 2016-08-04,2016-08-05,2016-08-08,2016-08-09,2016-08-10,2016-08-11,2016-08-12,2016-08-15,2016-08-16,2016-08-17 |
+---------------------------------------------------------------------------------------------------------------+
1 row in set (1.10 sec) mysql> select F_getBuluDates(10687);
+---------------------------------------------------------------------------------------------------------------+
| F_getBuluDates(10687) |
+---------------------------------------------------------------------------------------------------------------+
| 2016-08-04,2016-08-05,2016-08-08,2016-08-09,2016-08-10,2016-08-11,2016-08-12,2016-08-15,2016-08-16,2016-08-17 |
+---------------------------------------------------------------------------------------------------------------+
1 row in set (13.10 sec)
性能提升了 11 倍多。客户对效果很满意。
新的执行计划:
mysql> explain SELECT DISTINCT a2104 FROM a21 WHERE issubmit='y' AND a2104 < '2016-08-16' AND a2104 >= '2016-08-10' ORDER BY a2104 DESC;
+----+-------------+-------+-------+----------------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+----------------------+----------------+---------+------+------+--------------------------+
| 1 | SIMPLE | a21 | range | a2104,issubmit_a2104 | issubmit_a2104 | 516 | NULL | 834 | Using where; Using index |
+----+-------------+-------+-------+----------------------+----------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
总结:
从原函数实现来看,在数量小时,还是可以的。但是一旦数据量大了,就会出现性能问题。主要问题是在 where 条件中使用了太多的自定义函数,而且这个函数的参数还是表的数据列。
导致无法使用索引。无法使用索引就会进行全表扫描,所以数据量大时会导致性能问题。
MySQL优化一例的更多相关文章
- MySQL派生表(derived)优化一例
1.什么是派生表derived 关键字:子查询–>在From后where前的子查询 mysql; +----+-------------+------------+------+-------- ...
- MySQL优化聊两句
原文地址:http://www.cnblogs.com/verrion/p/mysql_optimised.html MySQL优化聊两句 MySQL不多介绍,今天聊两句该如何优化以及从哪些方面入手, ...
- MySQL优化实例
这周就要从泰笛离职了,在公司内部的wiki上,根据公司实际的项目,写了一些mysql的优化方法,供小组里的小伙伴参考下,没想到大家的热情很高,还专门搞了个ppt讲解了一下. 举了三个大家很容易犯错的地 ...
- [转] MySql 优化 大数据优化
一.我们可以且应该优化什么? 硬件 操作系统/软件库 SQL服务器(设置和查询) 应用编程接口(API) 应用程序 ------------------------------------------ ...
- (转)MySQL优化实例
在Apache, PHP,MySQL的体系架构中,MySQL对于性能的影响最大,也是关键的核心部分.对于Discuz!论坛程序也是如此,MySQL的设置是否合理优化,直接影响到论坛的速度和承载量!同时 ...
- MYSQL 优化建议
转自 http://coolshell.cn/articles/1846.html MYSQL 优化建议20条 1. 为查询缓存优化你的查询 大多数的MySQL服务器都开启了查询缓存.这是提高性最有效 ...
- MySQL优化四(优化表结构)
body { font-family: Helvetica, arial, sans-serif; font-size: 14px; line-height: 1.6; padding-top: 10 ...
- MySQL优化面试
原则:尽量使用整型表示字符串 存储IP INET_ATON(str),address to number INET_NTOA(number),number to address MySQL内部的枚举类 ...
- MySQL优化之my.conf配置详解
最近项目不太忙,所以有时间静心来研究下mysql的优化,对于MySQL的设置是否合理优化,直接影响到网站的速度和承载量!同时,MySQL也是优化难度最大的一个部分,不但需要理解一些MySQL专业知识, ...
随机推荐
- ABP框架 - 启动配置
文档目录 本节内容: 配置ABP 替换内置服务 配置模块 为一个模块创建配置 ABP在启动时,提供基础框架和模型来配置和模块化. 置ABP 在预初始化事件中进行配置,示例: kid1412注:XmlL ...
- vagrant 1.8.6 安装过程及总结遇到的坑
下面先总结遇到的问题,这些问题如果你也遇到,可能需要搜索很多次才能找到原因. 如果想看安装过程,可以先直接跳到后面第二部分部分. 1 问题汇总: 1.1 vagrant版本过高问题. vagrant ...
- 非阻塞/异步(epoll) openssl
前段时间在自己的异步网络框架handy中添加openssl的支持,当时在网络上搜索了半天也没有找到很好的例子,后来自己慢慢的摸索,耗费不少时间,终于搞定.因此把相关的资料整理一下,并给出简单的例子,让 ...
- CI Weekly #5 | 微服务架构下的持续部署与交付
CI Weekly 围绕『 软件工程效率提升』 进行一系列技术内容分享,包括国内外持续集成.持续交付,持续部署.自动化测试. DevOps 等实践教程.工具与资源,以及一些工程师文化相关的程序员 Ti ...
- div中设置滚动条的问题
<div srtle="width:100px;height:50px;"></div> 这样的一个div,当文本超出的时候我们就会设: overflow: ...
- 解决“Dynamic Web Module 3.0 requires Java 1.6 or newer.”错误
一.问题描述 1.错误截图如下. 2.设计的问题 在Eclipse中新建了一个Maven工程, 然后更改JDK版本为1.6, 结果每次使用Maven > Update project的时候JDK ...
- 详解:基于WEB API实现批量文件由一个服务器同步快速传输到其它多个服务器功能
文件同步传输工具比较多,传输的方式也比较多,比如:FTP.共享.HTTP等,我这里要讲的就是基于HTTP协议的WEB API实现批量文件由一个服务器同步快速传输到其它多个服务器这样的一个工具(简称:一 ...
- 如果你也会C#,那不妨了解下F#(2):数值运算和流程控制语法
本文链接:http://www.cnblogs.com/hjklin/p/fs-for-cs-dev-2.html 一些废话 一门语言火不火,与语言本身并没太大关系,主要看语言的推广. 推广得好,用的 ...
- 学习javascript数据结构(二)——链表
前言 人生总是直向前行走,从不留下什么. 原文地址:学习javascript数据结构(二)--链表 博主博客地址:Damonare的个人博客 正文 链表简介 上一篇博客-学习javascript数据结 ...
- Node学习笔记(三):基于socket.io web版你画我猜(一)
经过惨淡的面试,也是知道了自己的不足,刚好最近在学习node,心中便有了做一个web版的你画我猜的想法 首先说下思路,在做准备工作的时候,有两个大概的思路: 1.规定一块div,捕捉鼠标事件,动态生成 ...