SQL笔记 -- 查询优化

1. 关联查询优化

1.1 驱动表和被驱动表

对于内连接来说,优化器会根据用户的查询语句做优化,决定先查哪张表。先查询的那张表就是驱动表,反之就是被驱动表。而对于外连接来说,大多数情况用户指定的主表就是驱动表,但优化器也会视情况进行选择。

1.2 Simple Nested-Loop Join (简单嵌套循环连接)

从表A中取出一条数据,遍历表B,将匹配到的数据放到result… 以此类推,驱动表A中的每一条记录与被驱动表B的记录进行判断。可以看到这种方式效率是非常低的,以上述表A数据100条,表B数据1000条计算,则A*B=10万次。

1.3 Index Nested-Loop Join (索引嵌套循环连接)

其优化的思路主要是为了减少内存表数据的匹配次数,所以要求被驱动表上必须有索引才行。通过外层表匹配条件直接与内层表索引进行匹配,避免和内存表的每条记录去进行比较,这样极大的减少了对内存表的匹配次数。

驱动表中的每条记录通过被驱动表的索引进行访问,因为索引查询的成本是比较固定的,故mysql优化器都倾向于使用记录数少的表作为驱动表(外表)。

如果被驱动表加索引,效率是非常高的,但如果索引不是主键索引,所以还得进行一次回表查询。相比,被驱动表的索引是主键索引,效率会更高。

1.4 Block Nested-Loop Join(块嵌套循环连接)

其优化思路为一次性缓存多条数据,把参与查询的列缓存到Join Buffer 里,然后拿join buffer里的数据批量与内层表的数据进行匹配,从而减少了内层循环的次数(遍历一次内层表就可以批量匹配一次Join Buffer里面的外层表数据)。当不使用Index Nested-Loop Join的时候,默认使用Block Nested-Loop Join。

1.5 总结

(1)整体效率比较:INLJ > BNLJ > SNLJ

(2)永远用小结果集驱动大结果集(其本质就是减少外层循环的数据数量)(小的度量单位指的是表行数 * 每行大小)

(3)为被驱动表匹配的条件增加索引(减少内存表的循环匹配次数)

(4)增大join buffer size的大小(一次索引的数据越多,那么内层包的扫描次数就越少)

(5)减少驱动表不必要的字段查询(字段越少,join buffer所缓存的数据就越多)

(6)从MySQL的8.0.20版本开始将废弃BNLJ,因为从MySQL8.0.18版本开始就加入了hash join默认都会使用hash join

1.6 优化思路

  • 保证被驱动表的JOIN字段已经创建了索引
  • 需要JOIN 的字段,数据类型保持绝对一致。
  • LEFT JOIN 时,选择小表作为驱动表, 大表作为被驱动表 。减少外层循环的次数。
  • INNER JOIN 时,MySQL会自动将 小结果集的表选为驱动表 。选择相信MySQL优化策略。
  • 能够直接多表关联的尽量直接关联,不用子查询。(减少查询的趟数)
  • 不建议使用子查询,建议将子查询SQL拆开结合程序多次查询,或使用 JOIN 来代替子查询。

2. 子查询优化

子查询是 MySQL 的一项重要的功能,可以帮助我们通过一个 SQL 语句实现比较复杂的查询。但是,子查询的执行效率不高。原因:

① 执行子查询时,MySQL需要为内层查询语句的查询结果 建立一个临时表 ,然后外层查询语句从临时表 中查询记录。查询完毕后,再 撤销这些临时表 。这样会消耗过多的CPU和IO资源,产生大量的慢查询。

② 子查询的结果集存储的临时表,不论是内存临时表还是磁盘临时表都 不会存在索引 ,所以查询性能会 受到一定的影响。

③ 对于返回结果集比较大的子查询,其对查询性能的影响也就越大。

在MySQL中,可以使用连接(JOIN)查询来替代子查询。连接查询不需要建立临时表,其速度比子查询要快 ,如果查询中使用索引的话,性能就会更好。

3. 排序优化

在MySQL中,支持两种排序方式,分别是FileSort和Index排序。Index 排序中,索引可以保证数据的有序性,不需要再进行排序,效率更高。FileSort 排序则一般在内存中进行排序,占用CPU较多。如果待排结果较大,会产生临时文件 I/O 到磁盘进行排序的情况,效率较低。

优化思路:

(1) SQL 中,可以在 WHERE 子句和 ORDER BY 子句中使用索引,目的是在 WHERE 子句中避免全表扫描 ,在 ORDER BY 子句 避免使用 FileSort 排序 。当然,某些情况下全表扫描,或者 FileSort 排序不一定比索引慢。但总的来说,我们还是要避免,以提高查询效率。

(2)尽量使用 Index 完成 ORDER BY 排序。如果 WHERE 和 ORDER BY 后面是相同的列就使用单索引列; 如果不同就使用联合索引。

(3)无法使用 Index 时,需要对 FileSort 方式进行调优。

4. GROUP BY优化

  • group by 使用索引的原则几乎跟order by一致 ,group by 即使没有过滤条件用到索引,也可以直接使用索引。
  • group by 先排序再分组,遵照索引建的最佳左前缀法则
  • 当无法使用索引列,增大 max_length_for_sort_data 和 sort_buffer_size 参数的设置
  • where效率高于having,能写在where限定的条件就不要写在having中了
  • 减少使用order by,和业务沟通能不排序就不排序,或将排序放到程序端去做。Order by、group by、distinct这些语句较为耗费CPU,数据库的CPU资源是极其宝贵的。
  • 包含了order by、group by、distinct这些查询的语句,where条件过滤出来的结果集请保持在1000行 以内,否则SQL会很慢。

5. 分页查询优化

优化思路一
在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。

优化思路二
该方案适用于主键自增的表,可以把Limit 查询转换成某个位置的查询 。例如:

--优化前
SELECT * FROM student LIMIT 2000000,10;
--优化后
SELECT * FROM student WHERE id > 2000000 LIMIT 10;

6. 优先使用覆盖索引

索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据;当能通过读取索引就可以得到想要的数据,那就不需要读取行了。一个索引包含了满足查询结果的数据就叫做覆盖索引。

正因为覆盖索引包含了满足查询结果的所有数据,因此就可以省去回表的操作,从而大大提升效率。

7. 索引下推ICP

索引下推(Index Condition Pushdown, ICP)是MySQL 5.6中新特性,是一种在存储引擎层使用索引过滤数据的一种优化方式。当使用索引条件下推是,EXPLAIN语句输出结果中Extra列内容显示为Using index condition。

索引下推就是指在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数来提高查询效率。

# 打开索引下推
SET optimizer_switch = 'index_condition_pushdown=on';

# 关闭索引下推
SET optimizer_switch = 'index_condition_pushdown=off';
ICP的使用条件

(1)如果表的访问类型为 range 、 ref 、 eq_ref 或者 ref_or_null 可以使用ICP。
(2)ICP可以使用InnDB和MyISAM表,包括分区表InnoDB和MyISAM表
(3) 对于InnoDB表,ICP仅用于二级索引。ICP的目标是减少全行读取次数,从而减少I/O操作。
(4)当SQL使用覆盖索引时,不支持ICP优化方法。因为这种情况下使用ICP不会减少I/O。
5. 相关子查询的条件不能使用ICP

你可能感兴趣的:(SQL,sql,spring)