MySQL 优化:优化 SQL 语句:优化 SELECT 语句

来自Wikioe
Eijux讨论 | 贡献2021年4月25日 (日) 13:36的版本 →‎WHERE 子句优化
跳到导航 跳到搜索


关于

查询以“SELECT”语句的形式执行数据库中的所有查找操作。

  • 除了“SELECT”语句外,查询的调整技术还适用于:“CREATE TABLE...AS SELECT”,“INSERT INTO...SELECT”和“DELETE”语句中的“WHERE”子句。这些语句还有其他性能方面的考虑,因为它们将写操作与面向读取的查询操作结合在一起。
  • NDB Cluster 支持 join 下推优化,将符合条件的 join 整体发送到 NDB Cluster 数据节点,在这些节点之间可以分布并并行执行。【???】


优化查询的主要注意事项是:

  1. 要使慢速“SELECT ... WHERE”查询更快,首先要检查的是是否可以添加index。在“WHERE”子句中使用的列上设置索引,以加快评估,过滤和最终检索结果的速度。为了避免浪费磁盘空间,请构造一小组索引,以加快应用程序中使用的许多相关查询的速度。
    对于使用“joins”和“foreign keys”之类的功能引用不同表的查询,索引尤为重要。您可以使用“EXPLAIN”语句来确定 SELECT 使用哪些索引。
  2. 隔离并调整查询中花费时间过多的任何部分,例如函数调用。根据查询的结构方式,可以对结果集中的每一行调用一次函数,甚至可以对表中的每一行调用一次函数,从而极大地提高了效率。
  3. 最小化查询中的全表扫描数量,尤其是对于大表。
  4. 通过定期使用“ANALYZE TABLE”语句来使表统计信息保持最新,因此优化器具有构造有效执行计划所需的信息。
  5. 了解每个表的存储引擎特定的调整技术,索引技术和配置参数。InnoDB 和 MyISAM 都有一套准则,用于启用和维持查询的高性能。
  6. 避免以难以理解的方式转换查询,尤其是在优化程序自动执行某些相同转换的情况下。【?】
  7. 如果使用基本准则之一不能轻松解决性能问题,请通过阅读“EXPLAIN”计划并调整索引,WHERE 子句,join 子句等来调查特定查询的内部详细信息。
    • (当您达到一定的专业水平时,阅读 EXPLAIN 计划可能是每个查询的第一步。)【!】
  8. 调整 MySQL 用于缓存的内存区域的大小和属性。通过有效地使用InnoDB buffer pool(缓冲池),MyISAM 键高速缓存和 MySQL 查询高速缓存,重复查询的运行速度更快,因为第二次及以后都从内存中检索结果。
  9. 即使对于使用缓存区域快速运行的查询,您也可能会进一步优化,以使它们需要更少的缓存,从而使您的应用程序更具可伸缩性。可伸缩性意味着您的应用程序可以处理更多的并发用户,更大的请求等,而不会导致性能大幅下降。
  10. 处理问题,其中其他会话同时访问表可能会影响查询速度。

WHERE 子句优化【适用于“SELECT”、“DELETE”和“UPDATE”语句中的“WHERE”子句】

您可能会试图重写查询以加快算术运算,同时牺牲可读性。

  • 因为MySQL会自动进行类似的优化,所以通常可以避免这项工作,并以更易于理解和维护的形式保留查询。


MySQL 执行的一些优化如下:

  1. 删除不必要的括号:
    ((a AND b) AND c OR (((a AND b) AND (c AND d))))
    -> (a AND b AND c) OR (a AND b AND c AND d)
    
  2. 常数折叠:
    (a<b AND b=c) AND a=5
    -> b>5 AND b=c AND a=5
    
  3. 常数条件移除:
    (b>=5 AND b=5) OR (b=6 AND 5=5) OR (b=7 AND 5=6)
    -> b=5 OR b=6
    
  4. 索引使用的常量表达式仅计算一次。
  5. “COUNT(*)”:在没有“WHERE”单表上,是直接从 MyISAM 和 MEMORY 表的表信息中检索的。
    • 当其仅与一个表一起使用时,对于任何“NOT NULL”表达式也将执行此操作。
  6. 早期检测无效常量表达式。MySQL很快检测到一些“SELECT”语句是不可能的,并且不返回任何行。
  7. 如果不使用“GROUP BY”或集合函数(“COUNT()”、“MIN()”等),“HAVING”将与“WHERE”合并
  8. 对于联接中的每个表,构造一个更简单的“WHERE”来快速计算表的“WHERE”,并尽快跳过行。
  9. 在查询中的所有其他表之前,首先读取所有常量表。常量表可以是以下任意一个:【???】
    1. 空表或具有一行的表。
    2. 在“主键”或“唯一索引”上使用“WHERE”子句的表,其中所有索引部分都与常量表达式进行比较,并定义为“NOT NULL”。【?】
    以下所有表均用作常量表:
    SELECT * FROM t WHERE primary_key=1;
    SELECT * FROM t1,t2
      WHERE t1.primary_key=1 AND t2.primary_key=t1.id;
    
  10. 通过尝试所有可能的方法,找到用于联接表的最佳联接组合。如果“ORDER BY”和“GROUP BY”子句中的所有列都来自同一表,则在联接时优先使用该表。
  11. 如果有一个“ORDER BY”子句和另一个“GROUP BY”子句,或者“ORDER BY”或“GROUP BY”包含联接队列中第一个表以外的表中的列,则会创建一个临时表。【???】
  12. 如果使用“SQL_SMALL_RESULT”修饰符,则 MySQL 使用内存中的临时表。【???】
  13. 查询每个表索引,并使用最佳索引,除非优化程序认为使用全表扫描更有效。同时,使用扫描是基于最佳索引是否跨越了表的 30%以上,但是固定百分比不再决定使用索引还是扫描。现在,优化器更加复杂,其评估基于其他因素,例如表大小,行数和 I/O 块大小。
  14. 在输出每一行之前,将跳过与“HAVING”子句不匹配的行。


快速查询示例:

SELECT COUNT(*) FROM tbl_name;

SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name;

SELECT MAX(key_part2) FROM tbl_name
  WHERE key_part1=constant;

SELECT ... FROM tbl_name
  ORDER BY key_part1,key_part2,... LIMIT 10;

SELECT ... FROM tbl_name
  ORDER BY key_part1 DESC, key_part2 DESC, ... LIMIT 10;

假设索引列是数字,MySQL 仅使用索引树来解析以下查询:

SELECT key_part1,key_part2 FROM tbl_name WHERE key_part1=val;

SELECT COUNT(*) FROM tbl_name
  WHERE key_part1=val1 AND key_part2=val2;

SELECT key_part2 FROM tbl_name GROUP BY key_part1;

以下查询使用索引来按排序 Sequences 检索行,而无需单独的排序遍历:

SELECT ... FROM tbl_name
  ORDER BY key_part1,key_part2,... ;

SELECT ... FROM tbl_name
  ORDER BY key_part1 DESC, key_part2 DESC, ... ;

范围优化

索引合并优化

引擎调教下推优化【???】

索引条件下推优化【???】

嵌套循环连接算法

嵌套联接优化

Outer Join 优化

Outer Join 简化

多范围读取优化

阻止嵌套循环和批量密钥访问联接

条件过滤

IS NULL 优化

ORDER BY 优化

GROUP BY 优化

DISTINCT 优化

LIMIT 查询优化

函数调用优化

行构造函数表达式优化

避免全表扫描