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专业知识, ...
随机推荐
- HTML (超文本标记语言)
<html> --开始标签 <head> 网页上的控制信息 <title>页面标题</title> </head> <body> ...
- 基于Hexo和Github搭建博客
搭建自己的个人博客. 准备工作 确保电脑需要已下载安装node和npm.查看安装是否成功,windows只需在命令行输入以下两条命令即可. 1 2 $ node -v $ npm -v 安装hexo ...
- Electron安装
1.安装nodejs和npm 官网下载地址:https://nodejs.org/en/download/ 安装包:下载.msi 安装完成后: nodejs.npm都会安装好,path环境变量也自动设 ...
- 【目录】本博客其他.NET开源项目文章目录
本博客所有文章分类的总目录链接:本博客博文总目录-实时更新 1.本博客其他.NET开源项目文章目录 37..NET平台开源项目速览(17)FluentConsole让你的控制台酷起来 36..NET平 ...
- YYModel 源码解读(一)之YYModel.h
#if __has_include(<YYModel/YYModel.h>) FOUNDATION_EXPORT double YYModelVersionNumber; FOUNDATI ...
- 『.NET Core CLI工具文档』(七)dotnet-new
说明:本文是个人翻译文章,由于个人水平有限,有不对的地方请大家帮忙更正. 原文:dotnet-new 翻译:dotnet-new 名称 dotnet-new -- 创建一个新的 .NET Core 项 ...
- Button 模板和样式
<Style TargetType="{x:Type Button}"> <Setter Property="FontFamily" Valu ...
- Xcode7.1环境下上架iOS App到AppStore 流程③(Part 三)
前言部分 part三 部分主要讲解 Xcode关联绑定发布证书的配置.创建App信息.使用Application Loader上传.ipa文件到AppStore 一.Xcode配置发布证书信息 1)给 ...
- 【Java每日一题】20161223
package Dec2016; public class Ques1223 { public static void main(String[] args){ Integer obj = Integ ...
- openresty 前端开发入门二
这一章主要介绍介绍怎么获取请求参数,并且处理之后返回数据 我们知道http请求通常分为两种,分别是GET,POST,在http协议中,GET参数通常会紧跟在uri后面,而POST请求参数则包含在请求体 ...