MySQL SQL优化小结

系统 1740 0

优化是一个综合复杂的问题,涉及到硬软件平台。这里仅就 MySQL5.0 SQL 查询语句方面作个小结。先举个例子:

现在表register(PRIMARY KEY regId),service(PRIMARY KEY servId, index serviceId )和subscribe(PRIMARY KEY subId,FOREIGN KEY regId/servId),且前两表跟后张表均为一对多关联,假设有如下表格结构:

    CREATE TABLE `subscribe` (
  `subId` int(10) unsigned NOT NULL auto_increment,
  `subcribeTime` datetime NOT NULL,
  `expireTime` datetime NOT NULL,
  `cancelTime` datetime default NULL,
  `paymoney` double NOT NULL,
  `paymentTime` datetime NOT NULL,
  `payWay` tinyint(3) unsigned NOT NULL,
  `subcribeStatus` tinyint(3) unsigned NOT NULL,
  `paymentStatus` tinyint(3) unsigned NOT NULL,
  `createTime` datetime NOT NULL,
  `regId` int(10) default NULL,
  `servId` int(10) default NULL,
  PRIMARY KEY  (`subId`),
  KEY `FK_REGID` (`regId`),
  KEY `FK_SERVID` (`servId`),
  CONSTRAINT `FK_REGID` FOREIGN KEY (`regId`) REFERENCES `register` (`regId`) 
ON DELETE CASCADE,
  CONSTRAINT `FK_SERVID` FOREIGN KEY (`servId`) REFERENCES `service` (`servId`) 
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  

现在一业务根据条件 subscribe.subcribeStatus=1 或 >=3 AND subscribe.expireTime <= now() 要取得 subscribe.subId,register.phoneNum,service.serviceId 三个字段信息列表。测试中三表中各有三条数据具结果亦为3,初始 sql 语句及执行结果如下:

这是一条最容易想到的 SQL 语句,虽然优化器默认采用 type 快速的 index eq_ref 类型,但 s u ref 均为 NULL 意味着将全表扫描,并 产生一个笛卡尔乘积。对于这个实例由于各表都有3行数据,即扫描 3*3*1=9 行。如果表中数据是99999,那么将是 99999*99999*1=? ,你可以想象它将花多长时间……,如果是关键业务这将是一场恶梦。 那有没什么办法尽最大可能地减少这个笛卡尔乘积呢?先看看优化器对上面最终执行的语句:

可以看到优化器将使用内连接来执行这个语句,这将是一个考虑的优化点,再看看这有许多 WHERE 条件,这可不可以做反应优化呢?

分析一下,可以看到这些 WHERE 条件中起实质限定作用的 均与 subscribe 这张表有关 ,那就从这开始吧, u.subcribeStatus>=3 OR u.subcribeStatus=1 这个语句看起来有点不顺眼 ( 可能是因为有个OR,呵呵 ~) ,既然1到3中只2除外,那有没可能去掉这个 OR 呢? 看一下业务需求,原来 subscribeStatus 只有1至4的值,很明显这个写法不妥。 可以改为 u.subcribeStatus <>2 其它条件看起来没什么问题。再来看看联接查询方面的。 既然所有限定条件都是 u 表的,那么自然想到查询应该从 u 表开始搜索,这可以使用左/右联接,看个人爱好。 看一下最终语句的执行结果:

可看到笛卡尔积变成了: 3*1*1=3 ,相对前者多了个 ref 引用,虽然已经很“完美”了,但不可避免有个表 type=all 意味着将根据条件进行全表扫描。再看看优化器的执行方式:

很显然,它按我们优化方向执行,先从 u 开始再联接 s / r (这两次序无所谓)查询。就这个示例,我们也只能优化到这里,看看笛卡积,效果还是相当不错的,少了一个数量级的扫描。根据 SHOW WARNINGS 最终 SQL 为( ON 条件中也可以不用括号):

    SELECT u.subId, r.phoneNum, s.serviceId FROM subscribe as u LEFT JOIN service as s
 ON (s.servId=u.servId) LEFT JOIN register as r ON (r.regId=u.regId)
 WHERE u.subcribeStatus<>=? AND u.expireTime <= now() LIMIT ?,?
  

 

通过上面示例,可以知道 MySQL 提供的一些工具非常实用,下面介绍一下刚才用过的:

1. GRANT 语句尽量简单,以降低不必要的许可检查开销;
2. 如果问题与具体 MYSQ L表达式或函数有关,可使用 BENCHMARK() 函数执行定时测试,语法: BENCHMARK(loop_count,expression) ;
3. EXPLAIN 可作为 DESCRIBE 的同义词,它将解释 MySQL 如何处理 SELECT 语句,提供有关表如何联接和联接的次序信息,这对优化 SQL 语句特别是级联查询时特别有用。

4. SHOW WARNINGS 可以浏览 EXTENDED 产生的附加信息,输出优化器重写并优化后的SELECT语句,可能还包括优化过程的其它注解。
EXPLAIN 语法及等价 SQL 语句如下:
EXPLAIN tbl_name / EXPLAIN [EXTENDED] SELECT select_options
EXPLAIN tbl_name = DESCRIBE tbl_name = SHOW COLUMNS FROM tbl_name


          何时须为表加入索引以得到更快的 SELECT
         /
主要用途:
         \
          知道优化器是否以一个最佳次序联接表

 

 

WHERE子句优化

1. MySQL 能更高效地在声明具有相同类型和尺寸的列上使用索引,所以在类型相同时尽量保持相同尺寸(如对于固定大小的使用char类型),如果数据经常修改的话 CHAR 要优于 VARCHAR 。因为定长的行并不会有存储残片。对于非常短的列, CHAR 要比 VARCHAR 高效。

 

2.去除不必要的括号

e.g. ((a AND b) AND c OR (((a AND b) AND (c AND d))))
   -> (a AND b AND c) OR (a AND b AND c AND d)

 

3. 常量重叠

e.g. (a < b AND b = c) AND a = 5
   -> b > 5 AND b = c AND a = 5

 

4. 去除常量条件(由于常量重叠需要 ):

 e.g. (B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6)
   -> B=5 OR B=6

 

5. 如果不使用GROUP BY或分组函数(COUNT()、MIN()……),HAVING与WHERE合并。

 

6. 对于联接内的每个表,构造一个更简单的WHERE以便更快地对表进行WHERE计算并且也尽快跳过记录。

 

7. 所有常数的表在查询中比其它表先读出。常数表为:
   空表或只有1行的表。
   与在一个PRIMARY KEY或UNIQUE索引的WHERE子句一起使用的表,这里所有的索引部分使用常数表达式并且索引部分被定义为NOT NULL。


e.g.下列的所有表用作常数表:
mysql> SELECT * FROM t WHERE primary_key=1;
mysql> SELECT * FROM t1,t2 WHERE t1.primary_key=1 AND t2.primary_key=t1.id

 

 

待续。。。。

 

MySQL SQL优化小结


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论