Mysql查询优化器的局限性和Hint

Mysql查询优化器的局限性和Hint

Posted by Lerko on September 16, 2020

局限性

  • 对关联子查询支持比较糟糕,特别是IN这种子查询。mysql 不会将数据查出来之后在进行二次查询,而是将子查询的逻辑压入外层查询。

比如 select * from outTable where cid in(select * from innerTable where cid=1)

会被改造成为 select * from outTable where exists (select * from cidTable where cid = 1 and outTable.cid = innerTable.cd )

  • union限制。有时候Mysql无法将限制条件从外层下推到内层

比如

(select * from t1)union all (select * from t2) limit 20

需要在每个内层加上限制条件

(select * from t1 limit 20)union all (select * from t2 limit 20) limit 20

  • 并行执行

mysql 无法利用多核心来进行并行执行查询

  • 表join无法使用hash关联,但是其中MariaDb这个引擎实现了真正的Hash关联

  • 松散索引扫描 [高性能Mysql 230页]

mysql 一般无法支持松散索引扫描,只支持紧凑索引扫描

这也是mysql有最左前缀匹配的原因

在5.0之后下列情况可以支持

在下面一些情况下是可以使用松散索引扫描的:

  • 查询针对一个单表。
  • GROUP BY包括索引的第1个连续部分(如果对于GROUP BY,查询有一个DISTINCT子句,则所有DISTINCT的属性指向索引开头)。
  • 只使用累积函数(如果有)MIN()和MAX(),并且它们均指向相同的列。
  • 索引的任何其它部分(除了那些来自查询中引用的GROUP BY)必须为常数(也就是说,必须按常量数量来引用它们),但MIN()或MAX() 函数的参数例外。

# 查询优化器提示

优先操作 HIGH_PRIORITY

HIGH_PRIORITY可以使用在select和insert操作中,让MYSQL知道,这个操作优先进行。

SELECT HIGH_PRIORITY * FROM TABLE1;

滞后操作 LOW_PRIORITY

LOW_PRIORITY可以使用在select,delete,insert和update操作中,让mysql知道,这个操作滞后。

update LOW_PRIORITY table1 set field1= where field1= …

HIGH_PRIORITY LOW_PRIORITY 这两个提示都只在基于表锁的存储引擎非常有效。在innoDB和其他基于行锁的存储引擎,你可能永远用不上。在MyISAM中使用它们时,也要十分小心,因为它们会让并发插入失效,可能会严重下降性能。

延时插入 DELAYED

这个操作只能用于 insert 和 replace

INSERT DELAYED INTO table1 set field1= 

INSERT DELAYED INTO,是客户端提交数据给MySQL,MySQL返回OK状态给客户端。而这是并不是已经将数据插入表,而是存储在内存里面等待排队。 当mysql有 空余时,再插入。另一个重要的好处是,来自许多客户端的插入被集中在一起,并被编写入一个块。这比执行许多独立的插入要快很多。 坏处是,不能返回自动递增 的ID,以及系统崩溃时,MySQL还没有来得及插入数据的话,这些数据将会丢失。并且导致last_insert_id()无法正常工作。

强制连接顺序straight_join

SELECT TABLE1.FIELD1, TABLE2.FIELD2 FROM TABLE1 STRAIGHT_JOIN TABLE2 WHERE...;

由上面的SQL语句可知,通过STRAIGHT_JOIN强迫MySQL按TABLE1、TABLE2的顺序连接表。如果你认为按自己的顺序比MySQL推荐的顺序进行连接的效率高的话,就可以通过STRAIGHT_JOIN来确定连接顺序。

分组使用临时表 SQL_BIG_RESULT和SQL_SMALL_RESULT

SELECT SQL_BUFFER_RESULT FIELD1, COUNT(*) FROM TABLE1 GROUP BY FIELD1;

这两个提示只对select语句有效,它们告诉优化器对 group by 或者 distinct 查询如何使用临时表及排序。 sql_small_result 告诉优化器结果集会很小,可以将结果集放在内存中的索引临时表,以避免排序操作; sql_big_result 则告诉优化器结果集会很大,建议使用磁盘临时表做排序操作;

强制使用临时表sql_buffer_result

SELECT SQL_BUFFER_RESULT * FROM TABLE1 WHERE ;

这个提示告诉优化器将查询放入到一个临时表,然后尽可能地释放锁。这和前面提到的由客户端缓存结果不同。当你设法使用客户端缓存的时候,使用服务器端的缓存通常很有效。 带来的好处是无须在客户端消耗太多的内存,还可以尽可能快的释放对应的表锁。代价是,服务器端需要更多的内存。