MySQL性能优化(六)优化or条件
优化器是数据库中非常核心,又非常复杂的一个组件。有的SQL,优化器选择的执行计划并不是最优的,通过改写SQL,可以帮助优化器找到最优的执行计划。
where条件中的or子句,是比较容易出问题的一个场景。
一个例子
SELECT count(1) FROM car WHERE is_deleted = 0 AND car_id NOT IN ( SELECT car_id FROM product WHERE product_type = 2 AND source = 2) AND (seller_id = 100 OR (creator = 200 AND seller_id = -1))
执行计划
上面的SQL中,过滤性好的2个条件seller_id=100和creator = 200使用OR,
分别使用seller_id=100和creator=200过滤数据,再把数据和并起来,比单走seller_id的索引效果好。
mysql支持index_merge的执行计划。但是上面这个例子只用到了seller_id的索引。而seller_id=-1的过滤性差,从执行计划中也可以看到,car表的rows是16万,比较高。
这种情况下,默认的执行计划不够好,我们可以对SQL做一个小小的改造,改成这种形式:
SELECT sum(a) FROM ( SELECT count(1) as a FROM car WHERE is_deleted = 0 AND car_id NOT IN ( SELECT car_id FROM product WHERE product_type = 2 AND source = 2) AND (seller_id = 100) union all SELECT count(1) as a FROM car WHERE is_deleted = 0 AND car_id NOT IN (SELECT car_id FROM product WHERE product_type = 2 AND source = 2) AND creator = 1000 AND seller_id = -1 ) t
查看执行计划:
改写之后,union的2个SQL片段分别使用了2个不同的索引,扫描的行数分别是1238和7130,比改写之前的扫描16万行记录提升了一个数量级。
总结
虽然数据库的优化器非常强大,但是在有的场景下,改写SQL能帮助优化器更好地优化查询语句。也能让执行计划更稳定。