概念描述
MySQL有一些表级别的Hint,利用这些Hint配合索引、数据倾斜等特点,有时可以大幅度的提高SQL性能,以达到优化目的。
表级优化提示(Table-Level Optimizer Hints),常用的Hint有4种:
- BKA, NO_BKA:启用或禁用BKA算法对表进行JOIN。
- BNL, NO_BNL:启用或禁用BNL算法对表进行JOIN。
- DERIVED_CONDITION_PUSHDOWN, NO_DERIVED_CONDITION_PUSHDOWN:启用或禁用派生表条件下推(从MySQL 8.0.22版本开始)
- MERGE, NO_MERGE:启用或禁用视图合并(如视图、子查询、CTE等)。
这里还要说明以下3点:
- 建议读者先了解下NLJ、BNL、BKA、HASH算法。
- 派生表条件下推,在其它关系型数据库中早已存在,而MySQl到8.0.22版本才引入,这个优化是常用的技巧之一。
- 视图合并,MySQL对于复杂些的子查询优化的不是很好,但此方法也是常用的技巧之一,类似于把SQL中的子查询展开成基表访问。
测试验证
创建测试数据如下:
drop table if exists t1;
CREATE TABLE t1 (
id int NOT NULL AUTO_INCREMENT,
name varchar(20) DEFAULT NULL,
age int DEFAULT NULL,
PRIMARY KEY (id),
KEY ix_age (age)
) ENGINE=InnoDB;
insert into t1(id, name, age) values(1, 'name1', 10);
insert into t1(id, name, age) values(2, 'name2', 10);
insert into t1(id, name, age) values(3, 'name3', 20);
insert into t1(id, name, age) values(4, 'name4', 20);
insert into t1(id, name, age) values(5, 'name5', 30);
insert into t1(id, name, age) values(6, 'name6', 30);
insert into t1(id, name, age) values(7, 'name3', 40);
insert into t1(id, name, age) values(8, 'name4', 40);
insert into t1(id, name, age) values(9, 'name5', 50);
insert into t1(id, name, age) values(10, 'name6', 50);
drop table if exists t2;
create table t2 like t1;
insert into t2 select * from t1;
mysql> select * from t1;
+----+-------+------+
| id | name | age |
+----+-------+------+
| 1 | name1 | 10 |
| 2 | name2 | 10 |
| 3 | name3 | 20 |
| 4 | name4 | 20 |
| 5 | name5 | 30 |
| 6 | name6 | 30 |
| 7 | name3 | 40 |
| 8 | name4 | 40 |
| 9 | name5 | 50 |
| 10 | name6 | 50 |
+----+-------+------+
10 rows in set (0.01 sec)
-- 查看optimizer_switch变量
mysql> show variables like 'optimizer_switch';
+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name | Value |
+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| optimizer_switch | index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=off,skip_scan=on,hash_join=on,subquery_to_derived=off,prefer_ordering_index=on,hypergraph_optimizer=off,derived_condition_pushdown=on |
+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
--
1. BKA, NO_BKA:启用或禁用BKA算法对表进行JOIN.
-- 启用BKA,从Extra列可看出被驱动表走了BKA算法
mysql> explain
-> select /*+ BKA(a, b) */ a.*
-> from t1 a
-> inner join t2 b
-> on a.age=cast(b.age as char)
-> ;
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------------------------------------+
| 1 | SIMPLE | b | NULL | index | NULL | ix_age | 5 | NULL | 10 | 100.00 | Using index |
| 1 | SIMPLE | a | NULL | ref | ix_age | ix_age | 5 | func | 2 | 100.00 | Using where; Using join buffer (Batched Key Access) |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------------------------------------+
2 rows in set, 1 warning (0.02 sec)
mysql> explain analyze
-> select /*+ BKA(a, b) */ a.*
-> from t1 a
-> inner join t2 b
-> on a.age=cast(b.age as char)
-> ;
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Batched key access inner join (actual time=19.460..27.760 rows=20 loops=1)
-> Batch input rows
-> Covering index scan on b using ix_age (cost=1.25 rows=10) (actual time=0.269..1.335 rows=10 loops=1)
-> Filter: (cast(a.age as double) = cast(cast(b.age as char charset utf8mb4) as double)) (actual time=17.501..24.526 rows=20 loops=1)
-> Multi-range index lookup on a using ix_age (age=cast(b.age as char charset utf8mb4)) (actual time=17.173..22.687 rows=20 loops=1)
|
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-- 启用BKA,但当优化器判断有更低成本的执行计划时,选择最低成本执行计划。
mysql> explain
-> select /*+ BKA(a, b) */ a.*
-> from t1 a
-> inner join t2 b
-> on a.age=b.age
-> ;
+----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-------------+
| 1 | SIMPLE | a | NULL | ALL | ix_age | NULL | NULL | NULL | 10 | 100.00 | Using where |
| 1 | SIMPLE | b | NULL | ref | ix_age | ix_age | 5 | modb.a.age | 2 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
-- 禁用BKA,由于被驱动表索引失效,从Extra看出执行计划走了NLJ算法,性能很差
mysql> explain
-> select /*+ NO_BKA(a, b) */ a.*
-> from t1 a
-> inner join t2 b
-> on a.age=cast(b.age as char)
-> ;
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | b | NULL | index | NULL | ix_age | 5 | NULL | 10 | 100.00 | Using index |
| 1 | SIMPLE | a | NULL | ref | ix_age | ix_age | 5 | func | 2 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------+
2 rows in set, 1 warning (0.00 sec)
mysql> explain analyze
-> select /*+ NO_BKA(a, b) */ a.*
-> from t1 a
-> inner join t2 b
-> on a.age=cast(b.age as char)
-> ;
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Nested loop inner join (cost=8.25 rows=20) (actual time=1.039..12.472 rows=20 loops=1)
-> Covering index scan on b using ix_age (cost=1.25 rows=10) (actual time=0.248..1.295 rows=10 loops=1)
-> Index lookup on a using ix_age (age=cast(b.age as char charset utf8mb4)), with index condition: (cast(a.age as double) = cast(cast(b.age as char charset utf8mb4) as double)) (cost=0.52 rows=2) (actual time=0.659..0.847 rows=2 loops=10)
|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.02 sec)
注意事项:
- BKA提示相当于set optimizer_switch=‘batched_key_access=on’; 默认为关闭。
- BKA提示开启时,优化器会考虑评估此算法的成本,但最终会选择最低成本的执行计划。
- BKA、NO_BAK提示优先级高于optimizer_switch变量。
2. BNL, NO_BNL:启用或禁用BNL算法对表进行JOIN。
-- mysql5.7版本,由于没有hash算法,当被驱动表没有合适索引时,使用BNL算法
mysql> explain
-> select /*+ BNL(a, b) */ a.*
-> from t1 a
-> inner join t2 b
-> on a.name=b.name
-> ;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 100.00 | NULL |
| 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 10.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
-- mysql5.7版本,当使用NO_BNL提示时,禁用BNL算法,只能使用NLJ嵌套循环
mysql> explain
-> select /*+ NO_BNL(a, b) */ a.*
-> from t1 a
-> inner join t2 b
-> on a.name=b.name
-> ;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 100.00 | NULL |
| 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
-- mysql8.0版本,由于hash比BNL算法成本更低,故优化器选择hash算法
mysql> explain
-> select /*+ BNL(a, b) */ a.*
-> from t1 a
-> inner join t2 b
-> on a.name=b.name
-> ;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
| 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 100.00 | NULL |
| 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 10.00 | Using where; Using join buffer (hash join) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+
2 rows in set, 1 warning (0.01 sec)
mysql> explain analyze
-> select /*+ BNL(a, b) */ a.*
-> from t1 a
-> inner join t2 b
-> on a.name=b.name
-> ;
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Inner hash join (b.`name` = a.`name`) (cost=11.50 rows=10) (actual time=3.589..4.820 rows=18 loops=1)
-> Table scan on b (cost=0.04 rows=10) (actual time=0.448..0.826 rows=10 loops=1)
-> Hash
-> Table scan on a (cost=1.25 rows=10) (actual time=0.446..0.842 rows=10 loops=1)
|
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
-- mysql8.0版本,当使用NO_BNL提示时,禁用hash算法,同时也禁用BNL算法
mysql> explain
-> select /*+ NO_BNL(a, b) */ a.*
-> from t1 a
-> inner join t2 b
-> on a.name=b.name
-> ;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 100.00 | NULL |
| 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.01 sec)
mysql> explain analyze
-> select /*+ NO_BNL(a, b) */ a.*
-> from t1 a
-> inner join t2 b
-> on a.name=b.name
-> ;
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Nested loop inner join (cost=13.75 rows=10) (actual time=1.216..16.920 rows=18 loops=1)
-> Table scan on a (cost=1.25 rows=10) (actual time=0.302..1.372 rows=10 loops=1)
-> Filter: (b.`name` = a.`name`) (cost=0.26 rows=1) (actual time=0.642..1.291 rows=2 loops=10)
-> Table scan on b (cost=0.26 rows=10) (actual time=0.278..0.772 rows=10 loops=10)
|
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.03 sec)
注意事项:
- BNL提示相当于set optimizer_switch=‘block_nested_loop=on’; 默认为开启
- BNL提示开启时,优化器会考虑评估此算法的成本,但最终会选择最低成本的执行计划。mysql8.0增加了HASH算法,它的成本一般比BNL算法成本更低。
- BNL、NO_BNL提示优先级高于optimizer_switch变量。
- BNL、NO_BNL提示可以启用/禁用HASH JOIN。
3. DERIVED_CONDITION_PUSHDOWN, NO_DERIVED_CONDITION_PUSHDOWN:启用或禁用派生表条件下推
-- 派生表条件下推默认开启,会考虑把子查询外层的条件推入到子查询内部
mysql> explain
-> SELECT /*+ DERIVED_CONDITION_PUSHDOWN() */ * FROM
-> (SELECT age, count(*) cnt FROM t1 GROUP BY age) AS dt
-> WHERE age > 50;
+----+-------------+------------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------+
| 1 | PRIMARY | | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 100.00 | NULL |
| 2 | DERIVED | t1 | NULL | range | ix_age | ix_age | 5 | NULL | 1 | 100.00 | Using where; Using index |
+----+-------------+------------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------+
2 rows in set, 1 warning (0.02 sec)
-- 从warning信息可以看出,SQL进行了改写
mysql> show warnings;
+-------+------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select /*+ DERIVED_CONDITION_PUSHDOWN(@`select#1`) */ `dt`.`age` AS `age`,`dt`.`cnt` AS `cnt` from (/* select#2 */ select `modb`.`t1`.`age` AS `age`,count(0) AS `cnt` from `modb`.`t1` where (`modb`.`t1`.`age` > 50) group by `modb`.`t1`.`age`) `dt` |
+-------+------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
-- 从最后一行 using ix_age over (50 explain analyze
-> SELECT * FROM
-> (SELECT age, count(*) cnt FROM t1 GROUP BY age) AS dt
-> WHERE age > 50;
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Table scan on dt (cost=3.17..3.17 rows=1) (actual time=0.793..0.793 rows=0 loops=1)
-> Materialize (cost=0.66..0.66 rows=1) (actual time=0.610..0.610 rows=0 loops=1)
-> Group aggregate: count(0) (cost=0.56 rows=1) (actual time=0.451..0.451 rows=0 loops=1)
-> Filter: (t1.age > 50) (cost=0.46 rows=1) (actual time=0.351..0.351 rows=0 loops=1)
-> Covering index range scan on t1 using ix_age over (50 explain
-> SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN() */ * FROM
-> (SELECT age, count(*) cnt FROM t1 GROUP BY age) AS dt
-> WHERE age > 50;
+----+-------------+------------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
| 1 | PRIMARY | | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 33.33 | Using where |
| 2 | DERIVED | t1 | NULL | index | ix_age | ix_age | 5 | NULL | 10 | 100.00 | Using index |
+----+-------------+------------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.01 sec)
-- 从第一行 Filter: (dt.age > 50) 可以看出,子查询先物化,最后才过滤条件
mysql> explain analyze
-> SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN() */ * FROM
-> (SELECT age, count(*) cnt FROM t1 GROUP BY age) AS dt
-> WHERE age > 50;
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: (dt.age > 50) (cost=3.36..3.62 rows=3) (actual time=3.762..3.762 rows=0 loops=1)
-> Table scan on dt (cost=3.51..5.88 rows=10) (actual time=2.896..3.510 rows=5 loops=1)
-> Materialize (cost=3.25..3.25 rows=10) (actual time=2.706..2.706 rows=5 loops=1)
-> Group aggregate: count(0) (cost=2.25 rows=10) (actual time=0.735..2.097 rows=5 loops=1)
-> Covering index scan on t1 using ix_age (cost=1.25 rows=10) (actual time=0.487..1.241 rows=10 loops=1)
|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
注意事项:
- DERIVED_CONDITION_PUSHDOWN提示相当于set optimizer_switch=‘derived_condition_pushdown=on’; 默认为开启
- DERIVED_CONDITION_PUSHDOWN提示开启时,优化器会考虑评估此算法的成本,但最终会选择最低成本的执行计划。
- DERIVED_CONDITION_PUSHDOWN、NO_DERIVED_CONDITION_PUSHDOWN提示优先级高于optimizer_switch变量。
- 派生表条件内推是常用的优化技巧,对子查询优化有较大的好处。
4. MERGE, NO_MERGE:启用或禁用视图合并(如视图、子查询、CTE等)
-- MERGE默认开启
mysql> explain SELECT /*+ MERGE(dt) */ * FROM (SELECT * FROM t1 where name='name1') AS dt;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
-- 从warning信息可以看出,优化器对SQL进行了改写,去掉了子查询
mysql> show warnings;
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select /*+ MERGE(`dt`@`select#1`) */ `modb`.`t1`.`id` AS `id`,`modb`.`t1`.`name` AS `name`,`modb`.`t1`.`age` AS `age` from `modb`.`t1` where (`modb`.`t1`.`name` = 'name1') |
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
-- NO_MERGE提示,可以看出,子查询被物化
mysql> explain SELECT /*+ NO_MERGE(dt) */ * FROM (SELECT * FROM t1 where name='name1') AS dt;
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | PRIMARY | | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 100.00 | NULL |
| 2 | DERIVED | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 10 | 10.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
注意事项:
- MERGE提示相当于set optimizer_switch=‘derived_merge=on’; 默认为开启
- MERGE提示开启时,优化器会考虑评估此合并子查询的成本,但最终会选择最低成本的执行计划。
- MERGE、NO_MERGE提示优先级高于optimizer_switch变量。
- 对于NO_MERGE提示,会产生物化临时表。
- 视图合并是常用的优化技巧,对子查询优化有较大的好处。
- 对于视图引用,视图定义中的ALGORITHM={MERGE|TEMPTABLE}子句优先于引用该视图的查询中指定的提示。
知识总结
本文主要介绍了MySQL表级别Hints的使用方法,总结如下:
- 当优化器评估成本不符合我们的期望时,可以通过启用/禁用表级Hint,配合索引、数据倾斜等来优化SQL。
- 要了解这些算法底层意义,才能更好的利用它。
- 对于子查询优化,MySQL的优化能力目前为止有限,但可以通过DERIVED_CONDITION_PUSHDOWN、MERGE提示来优化(默认为开启)。如果达不到预期,则要通过改写SQL来进一步优化。
参考文档
https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html#optimizer-hints-table-levelhttps://support.enmotech.com/article/3870/publish
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
依赖注入的使用 构造方法注入 这是将服务注入类的最常用方法,是将依赖项注入类的首选方式,也是微软推崇的模式。这样,除非提供了所有构造方法注入的依赖项,否则无法构造类,显示的声明了类必需的服务,使开发人员一目了然。 public class BookAppSer…