MySQL8.0 优化器介绍(三)


  • GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
  • GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。
  • 作者:    奥特曼爱小怪兽
  • 文章来源:GreatSQL社区原创


往期回顾

MySQL8.0 优化器介绍(一)

MySQL8.0 优化器介绍(二)


本篇将进一步深入介绍优化器相关的join优化

为更好的理解本篇内容需要提前看一下以下内容:

  1. 单表访问的方法,参考《MySQL 是怎样运行的:从根儿上理解 MySQL》第10章"单表访问方法"
  2. 更多select语句级别的优化细节 见(https://dev.mysql.com/doc/refman/8.0/en/select-optimization.html)

为了让读者对join优化 有更深的了解,章节里的sql例子,留了一些思考和动手的问题。可能大家得到的答案会不同,但探索未知的过程,方式应该是一样的。


join优化(Join Optimizations)

MySQL可以使用Join Optimizations来改进上次分享过的join algorithms,或者决定如何执行部分查询。本次主要介绍三种经常用到的join Optimizations,更多的 join type  见下面的链接:(https://dev.mysql.com/doc/refman/8.0/en/explain-output.html#explain-join-types)

index merge

通常MySQL只会对每个表使用一个索引。但是,如果对同一表中的多个列在where后有条件限制,并且没有覆盖所有列的单个索引,无论选哪个索引都不是最佳的。对于这些情况,MySQL支持索引合并 (index merge)。select  a,b,c from t  where a=x1 and b=x2 and c=x3,这种情况下,建立一个多列的复合索引 index_abc  比使用 index merge +index_a+index_b+index_c 性能更好。

Index merge 支持三种算法 见下表

查询计划使用index merge 时,会在explain sql 的 access type 列 有"index_merge",key 列会 包含所有参与merge的列, key_length 包含一个所用索引的最长关键部分的列表。举个Intersection例子:

Intersection

以下代码块注释中提到的知识点略多

##无论optimizer 是否选择 index merge 取决于index statistics. ## index statistics 是从哪个试图获得呢?mysql.innodb_index_stats 还是 information_schema.statistics ## 还是 information_schema.INNODB_SYS_TABLESTATS? ## 可以参考 https://www.cnblogs.com/ClassicMan/articles/15871403.html ## index_dive eq_range_index_dive_limit 这两个参数有什么作用? ##意味着即使返回相同STATEMENT_DIGEST_TEXT的sql查询语句, WHERE语句后面跟不同的值,得到的查询计划可能是不一样的 ##比如select * from people where name='唯一值'; ##select * from people where name='超级多的重复值' ## 同理index statistics 的改变会让同一个查询走不同的执行计划, ## 体现在 select a,b from t where a=1 and b=1 有时走了 index merges,有时没走。 CREATE TABLE `payment` ( `payment_id` smallint unsigned NOT NULL, `customer_id` smallint unsigned NOT NULL, `staff_id` tinyint unsigned NOT NULL, `rental_id` int(DEFAULT NULL, `amount` decimal(5,2) NOT NULL, `payment_date` datetime NOT NULL, `last_update` timestamp NULL, PRIMARY KEY (`payment_id`), KEY `idx_fk_staff_id` (`staff_id`), KEY `idx_fk_customer_id` (`customer_id`), KEY `fk_payment_rental` (`rental_id`) ) ENGINE=InnoDB; ## case1 等值查询 SELECT * FROM sakila.payment WHERE staff_id = 1 AND customer_id = 75; mysql> EXPLAIN SELECT * FROM sakila.payment WHERE staff_id = 1 AND customer_id = 75\G **************************** 1. row ***************************** id: 1 select_type: SIMPLE table: payment partitions: NULL type: index_merge possible_keys: idx_fk_staff_id,idx_fk_customer_id key: idx_fk_customer_id,idx_fk_staff_id key_len: 2,1 ref: NULL rows: 20 filtered: 100 Extra: Using intersect(idx_fk_customer_id,idx_fk_staff_id); Using where 1 row in set, 1 warning (0.0007 sec) mysql> EXPLAIN FORMAT=TREE SELECT * FROM sakila.payment WHERE staff_id = 1 AND customer_id = 75\G **************************** 1. row **************************** EXPLAIN: -> Filter: ((sakila.payment.customer_id = 75) and (sakila.payment.staff_id = 1)) (cost=14.48 rows=20) -> Index range scan on payment using intersect(idx_fk_customer_id,idx_fk_staff_id) (cost=14.48 rows=20) 1 row in set (0.0004 sec) ##注意"Index range scan on payment",两个等值查询条件,为啥触发了rang scan? ## case2 下面的sql范围查询也能用到index merge 吗?执行计划 自己下去测试验证 SELECT * FROM sakila.payment WHERE payment_id > 10 AND customer_id = 318;