zoukankan      html  css  js  c++  java
  • SQL语句的优化汇总

    官方文档:

    8.2 Optimizing SQL Statements

    Chapter 12 How MySQL Performs Different Selects

    Chapter 13 How MySQL Transforms Subqueries

    分析SQL的执行过程索引生效和失效的典型场景中,总结了sql查询的基本步骤和索引的使用。而对于INSERT、GROUP BY、ORDER BY、LIMIT、UNION、子查询、关联查询等常用操作,也有相应的优化方法。

    一、优化SELECT语句

    这里只列出了最常见的优化,更多的优化内容请参考官方文档:Optimizing SELECT Statements

    1.where子句优化

    官方文档:WHERE Clause Optimization

    这里总结where语句的优化,以select为例,但同样适用于update和delete语句。

    2.范围优化

    官方文档:Range Optimization

    3.索引合并优化

    官方文档:Index Merge Optimization

     

    4.ICP优化

    官方文档:Index Condition Pushdown Optimization

    是在mysql5.6中新增的。 

    5.Outer join优化

    官方文档:

    Nested Join Optimization

    Outer Join Optimization

    Outer Join Simplification

    6.Multi-Range Read优化

    官方文档:Multi-Range Read Optimization

     

    7.Is Null优化

    官方文档:IS NULL Optimization

    8.ORDER BY优化

    官方文档:ORDER BY Optimization

    Mysql中有两种排序方式

    • 1.通过有序的索引顺序扫描直接返回有序数据。不需要额外的排序,操作效率高。
    • 2.通过返回数据进行排序(称为Filesort排序)

    Filesort排序是否使用磁盘文件或临时表,取决于Mysql服务器对排序参数的设置和需要排序数据的大小。

    尽量减少额外的排序,通过索引直接返回有序数据。WHERE条件和ORDER BY使用相同的索引,并且ORDER BY的顺序和索引顺序相同,并且ORDER BY的字段都是升序或者都是降序。否则肯定需要额外的排序操作,这样就出现了Filesort。

    9.GROUP BY优化

    官方文档:GROUP BY Optimization

    待补充:《高性能mysql》6.7.4节

    MySQL默认对所有GROUP BY col,cl2…的字段进行排序(分组后排序)。

    如果查询需要使用GROUP BY,但不想对结果进行排序,则可以禁用排序,只需要指定ORDER BY NULL

    GROUP BY xxx ORDER BY NULL

    10.DISTINCT优化

    官方文档:DISTINCT Optimization

    11.limit查询优化

    官方文档:LIMIT Query Optimization

    limit 1000,20

    mysql在处理这条语句会排序取出前1020条记录后返回最后20条记录,而将前1000条记录全抛弃,这样的代价是非常高的。

    优化思路1:使用索引覆盖扫描

    优化此类分页查询的一个最简单的办法就是尽可能地使用索引覆盖扫描,而不是所有的列,然后再做一次关联操作再返回所需的列。

    // 优化前
    select film_id,description from film order by title limit 1000,20
    // 优化后
    select a.film_id, a.description from film a inner join (select film_id from film order by title limit 1000,20) b on a.film_id = b.film_id

    优化思路2:把limit m,n转换成limit n的查询

    把limit查询转换成某个位置的查询,也就是把limit m,n转换成limit n的查询。
    这只适合在排序字段不会出现重复值的特定情况。如果排序字段出现大量重复值,而仍进行这种优化,那么分页结果可能会丢失部分记录,不适用这种方式进行优化。

    #需要记录上一页的最后一条记录的id

    12.优化UNION

    UNION会消除重复的行,而UNION ALL不会消除重复行。

    Mysql总是通过创建并填充临时表的方式来执行UNION查询,因此很多优化策略在UNION查询中都没法很好地使用。经常需要手工地将where,limit,order by等子句“下推”到UNION的各个子查询中,以便优化器可以充分利用这些条件进行优化(例如,直接将这些字句冗余地下一份到各个子查询)

    如果没有ALL,Mysql会给临时表加上DISTINCT选项,这会导致整个临时表的数据做唯一性检查,这样做的代价是非常高的。即使有ALL关键字,Mysql仍然会使用临时表存储结果。事实上,Mysql总是将结果放入临时表,然后再取出返回给客户端。

    除非确实需要服务器消除重复的行,否则一定要使用UNION ALL,这点很重要。

    二、优化子查询、导出表

    官方文档:Optimizing Subqueries, Derived Tables, and View References

    1.优化子查询

    在MySQL5.5及以下版本中,子查询的实现目前还比较差,很难得到一个很好的执行计划,很多时候明明有索引可以利用,可QueryOptimizer就是不用。从MySQL官方给出的信息说,这一问题将在MySQL6.0中得到较好的解决,将会引入SemiJoin的执行计划,可MySQL6.0离我们投入生产环境使用恐怕还有很遥远的一段时间。所以,能不用子查询的时候就尽量不要使用子查询。

    在Mysql5.6中,子查询已经有了很大的改善,"能不用子查询的时候就尽量不要使用子查询"这个建议将可以被忽略了。

    (我还暂未发现mysql5.6子查询相关的权威说明)

    2.优化关联查询

    需要特别提到的几点:

    ①确保on或者USING子句上的列有索引。创建索引的时候要考虑到关联的顺序。如果表A和表B用列c做关联,如果优化器的关联顺序是B,A,那么就不需要在B表的对应列上建索引。一般来说,除非有其它理由,否则只需要在关联顺序中的第二个表的相应列上创建索引

    我的解析:

    如果有这样的关联:on A.c=B.c ,假设优化器的关联顺序是B,A,即用B表去关联A表,则应该在第二个表即A表的c列上建立索引,而不是B表的c列上建立索引。

    ②确保任何的GROUP BY和ORDER BY中的表达式只涉及到一个表中的列,这样Mysql才有可能使用索引来优化这个过程。

    我的解析:

    可能是下面这个意思吧,我也不太确定????????

    (假设a为t1和t2共有字段)
    
    GROUP BY t2.a,t1.b (不建议)
    GROUP BY t1.a,t1.b (建议)
    
    ORDER BY t2.a,t1.b (不建议)
    ORDER BY t1.a,t1.b (建议)

    三、优化数据改变语句

    1.优化INSERT语句

    官方文档:Optimizing INSERT Statements

    ①如果从同一客户端批量插入多条记录,使用以下的插入。

    insert into test values(1,2),(1,3),(1,4)……

    ②如果从不同客户端批量插入多条记录,可以使用INSERT DELAYED语句得到更高的速度。
    DELAYED的含义是让INSERT语句马上执行,其实数据都被放在内存的队列中,并没有真正写入磁盘,这比每条语句分别插入要快的多。LOW_PRIORITY刚好相反,在所有其他用户对表的读写完成后才进行插入。
    ③将索引文件和数据文件存放在不同磁盘上。(利用建表中的选项)
    ④如果批量插入,可以通过增加bulk_insert_buffer_size变量值来提高速度(仅对MyISAM表有效)
    ⑤当从一个文本文件装载一个表时,使用LOAD DATA INFILE。这通常比使用很多INSERT语句快20倍。(仅适合文本文件)

    2.插入大批量数据

    对于MyISAM表,当用load命令导入数据时,可以有以下几种方式提高导入效率。

    ALTER TABLE 表名 DISABLE KEYS;//关闭非唯一索引的更新
    ……
    导入数据
    ……
    ALTER TABLE 表名  ENABLE KEYS;//打开非唯一索引的更新

    在导入大量数据到一个非空的MyISAM表时,通过设置这两个命令可提高导入效率。

    在导入大量数据到一个空的MyISAM表时,默认就是先导入数据然后才创建索引,所以不用进行设置。

    对于Innodb表,有以下几种方式提高导入效率
    ①因为Innodb类型的表是按照主键的顺序来保存的,所以将导入的数据按照主键的顺序排序,可以有效的提高导入数据的效率。

    ②关闭唯一性校验

    SET UNIQUE_CHECKS=0;(关闭唯一性校验) 
    SET UNIQUE_CHECKS=1;(开启唯一性校验)
    show variables like 'UNIQUE_CHECKS';(查看唯一性校验的状态)

    ③关闭自动提交

    导入数据前,关闭自动提交。导入完成后,再开启。

    SET AUTOCOMMIT=0;(关闭自动提交) 
    SET AUTOCOMMIT=1;(开启自动提交)
    show variables like 'AUTOCOMMIT';(查看自动提交的状态)

    ④关闭外键约束

    SET foreign_key_checks=0;(关闭外键约束)
    SET foreign_key_checks=1;(打开外键约束)
    show variables like 'foreign_key_checks';(查看外键约束的状态)

    ⑤另外可以同时对mysql进行参数调优

    不同版本的mysql参数的默认值可能不同,可以参考mysql官方文档。

    常用的几个mysql参数项:(mysql5.6)

    • max_allowed_packet: 默认4M      (我设置为64M)
    • innodb_buffer_pool_size:默认128M,(我8g电脑设置为1g),配置多大合适请参考这篇文章

    我设置了上面两个参数后,导入速度大大加快了。

    我的补充:
    在开发中我们经常需要将导出的sql文件再导入到mysql数据库中,直接用mysql命令就比普通的source命令导入sql效率高的多。
    主要区别就是source是一条条执行的效率当然低的多,而mysql命令则是批量提交,处理效率高。

    // 需要在mysql安装目录/bin下执行
    mysql -h localhost -uroot -p --default-character-set=utf8  [数据库名] < D:xx.sql

    3.优化Update语句

    官方文档:Optimizing UPDATE Statements

    An update statement is optimized like a SELECT query with the additional overhead of a write. The speed of the write depends on the amount of data being updated and the number of indexes that are updated. Indexes that are not changed do not get updated.

    Another way to get fast updates is to delay updates and then do many updates in a row later. Performing multiple updates together is much quicker than doing one at a time if you lock the table.

    4.优化Delete语句

    官方文档:Optimizing DELETE Statements

    The time required to delete individual rows in a MyISAM table is exactly proportional to the number of indexes. To delete rows more quickly, you can increase the size of the key cache by increasing the key_buffer_size system variable. See Section 5.1.1, “Configuring the Server”.

    To delete all rows from a MyISAM table, TRUNCATE TABLE tbl_name is faster than DELETE FROM tbl_name. Truncate operations are not transaction-safe; an error occurs when attempting one in the course of an active transaction or active table lock. See Section 13.1.34, “TRUNCATE TABLE Statement”.

    参考资料:

    官方文档:Optimizing SQL Statements
    《深入浅出MySQL:数据库开发、优化与管理维护》第2版
    《高性能Mysql》第3版

  • 相关阅读:
    makefile实验二 对目标的深入理解 以及rebuild build clean的实现
    makefile实验一 make的基本原则、伪目标、以及不使用.PHONY确实现和伪目标一样功能的一种方法
    IP基础知识
    玩转Libmodbus(二) 写代码体验
    故意使用free掉的内存的一个实验( 常量区/栈)
    使用free掉的内存的危害
    数字签名 数字证书
    哈希
    初识Makefile
    约瑟夫问题及扩展问题的代码实现
  • 原文地址:https://www.cnblogs.com/rouqinglangzi/p/11144979.html
Copyright © 2011-2022 走看看