Mysql优化之 or 条件优化

最近,碰到一个线上数据同步脚本慢查询问题。

大致语句如下:

	       SELECT
				`e`.`id` AS `gid`,
				`a`.`id` AS `cid`,
				`a`.`chaname` AS `chan`,
				`b`.`id` AS `fid`,
				`a`.`create_time` AS `create_time`,
				`a`.`update_time` AS `update_time`
			FROM
				`chn` `a`
			INNER JOIN `fol` `b` ON a.foid = b.id
			LEFT JOIN `pack` `d` ON a.id = d.cid
			LEFT JOIN `gam` `e` ON d.gid = e.id
			WHERE
				a.update_time >= '2020-12-05 14:47:32' or
				b.update_time >= '2020-12-05 14:47:32' or 
				d.update_time >= '2020-12-05 14:47:32' or
				e.update_time >= '2020-12-05 14:47:32'

发现该语句,整条SQL需要消耗大量CPU和IO资源,且相应时间超长。

最终,选择方案sql:

 

参考其他文案原理如下:

索引合并(Index merge)的策略,一定程度上可以使用表上多个单列索引来定位指定的行。

 该特性主要应用于以下三种场景:

      1. 对or语句求并集,如查询select * from film_actor where c1 = “xxx” or c2 = “xxx”时,如果c1和c2列上分别有索引,可以按照c1和c2条件进行查询,再将查询结果合并(union)操作,得到最终结果。

      2. 对and语句求交集,如查询select * from film_actor where c1 = “xxx” and c2 = “xxx”时,如果c1和c2列上分别有索引,可以按照c1和c2条件进行查询,再将查询结果取交集(intersect)操作,得到最终结果。

      3. 组合前两种情况的合并及相交。

    该新特性可以在一些场景中大幅度提升查询性能,但受限于MySQL糟糕的统计信息,也导致很多查询场景查询性能极差甚至导致数据库崩溃。

      以select * from film_actor where c1 = “xxx” and c2 = “xxx”为例:

        1. 当c1列和c2列选择性较高时,按照c1和c2条件进行查询性能高且返回数据集较小,再对两个数据量较小的数据集求交集的操作成本也比较低,最终整个语句查询高效;

        2. 当c1列或c2列选择性较差且统计信息不准时,比如整表数据量1000万,按照c2列条件返回800万数据,按照c1列返回100条数据,此时按照c2列条件进行索引扫描+聚集索引查询的操作成本极高(可能是整表扫描的百倍消耗),对100

          条数据和800万数据求交集的成本也极高,最终导致整条SQL需要消耗大量CPU和IO资源,且相应时间超长,而如果值使用c1列的索引,查询消耗资源少且性能较高。

          即使用select * from film_actor where c1 = “xxx” union all select * from film_actor where  c2 = “xxx”往往更好。

      索引合并策略有时候是一种优化的结果,但实际上更多的时候说明了表上的索引建的的很糟糕:

        1、当出现服务器对多个索引做相交操作时(通常有多个and条件),通常意味着需要一个包含所有相关列的多列索引,而不是多个独立的单列索引。

        2、当服务器需要对多个索引做合并操作时(通常有多个or条件),通常需要消耗大量cpu和内存资源在算法缓存、排序和合并操作上。特别是当其中某些索引的选择性不高,需要合并扫描返回的大量数据的时候。

        3、更重要的是,优化器不会把这些计算到“查询成本”中,优化器只关心随机页面读取。这回使得查询的成本被“低估”,导致该执行计划还不如直接走全表扫描。

 

本文地址:https://blog.csdn.net/xmwh19996/article/details/110699553

(0)
上一篇 2022年3月21日
下一篇 2022年3月21日

相关推荐