|
nest loops |
hash join |
sort merge join |
原理 |
先返回第一个表的结果集,然后依次循环地用第一个表的结果集中的一条结果,去匹配第二个表中的数据。因此,第一个表取出多少条数据,第二个表就会被访问多少次。 |
先分别在两个表先hash运算后,缓存起来,使这些数据相对有序(但本身不排序),然后再通过比较相同的hash值整体进行表连接,所以每一个表最多将被访问一次。 |
先分别在两个表排序,然后再整体进行表连接,所以每一个表最多将被访问一次。sort merge join不分驱动表和被驱动表。 |
连接方式 |
支持各种连接(百搭) |
hash join绝大多数情况用于等值连接,不支持不等值连接。 |
绝大多数情况用于等值连接,只支持像>或<这样的不等值连接,不支持其他的不等值连接 |
适用条件 |
1 驱动行源数据量小(不一定)。 2, 被驱动表关联的列上有索引,建索引的列上重复率低(不一定)。
|
做等值连接,而且连接列没索引(不一定)。 |
1两个表返回的结果集都不太大(不一定)。 2两个结果集基本有序(排序的代价相对较小)(不一定) |
索引优化 |
发现有nested loops后,在where 语句的限制条件列上建索引,如果建完索引后,你发现还是nested loops,就在做连接的另一张表的连接字段建索引。 |
发现hash loops后,最好在驱动表的限制条件上建索引,如果建完索引后还是hash join,此时在另一张表的连接条件上建索引效果不明显。 可以不建连接条件的索引。 |
发现sort merge join后,最好在驱动表的连接条件上建索引,建好索引后,执行代价会降低,如果此时还是sort merge join的话,而另一张表数据也很多,可以考虑在另一张表的连接条件上也建立索引。 可以建连接条件双方表的索引。 |
相关参数 |
|
hash loops的优化还可以考虑增大hash_area_size的值,或者简单调整pga的大小,尽量在内存中完成hash动作。 |
sort merge loops还可以考虑增大sort_area_size的值,或简单调整pga的大小,尽量在内存完成排序。 |
SELECT |
select部分的字段越少效率越高,如果业务允许,查询字段就少一些。 1如果走了索引但出现了Table Access by ROWID(查询字段过多,只能通过rowid访问表的数据,称为索引回表),如果业务允许,可以减少select部分的字段为索引列,从而将执行计划改成index range scan(不访问表),达到优化的目的。 2如果业务必须要求某两三列必须出现,而索引只有一列,可以建复合索引,但是注意前导列必须是该条件,而且字段不能超过3个(最好2个)。实在要求很多字段必须出现,就保留索引回表,反正也算走索引了。 |
||
驱动表 |
1最右最上原则,先看缩进最右的行,同缩进的按照从上往下的顺序执行。先执行的就是驱动表(外部表),后执行的表就是被驱动表(内部表)。 2如果表连接的两列都没有索引或都有索引,连接时把返回较少行的表作为驱动表较好(nest loops和hash join最明显,merge join也略有提升)。 3不论表连接的方式如何,在被驱动表上列添加索引可以不同程度的减少表连接的代价(不论是不是大小表)。 |
||
自然状态 |
1没hint自然情况下,优化器选择的执行计划一般是hash join。 2如果只有一个表A有索引,另一表B无索引,无索引的表B通常作为驱动表,A表作为被驱动表。 |
||
F ROm |
1 RBO以从右到左的顺序处理表连接,也就是from 子句最右端table作为驱动表。 2CBO无所谓。最好养成好的习惯,写from时把小表放后,大表放前。 |
||
WHERE |
如果有多个条件,则按照从右向左顺序执行——A and 条件B and条件 C执行顺序是CBA,所以把最能筛选数据的列往后方,最好加索引。 |
||
聚合函数 |
1 如果有表中有非空约束列(not null)而且上面有索引,count,avg,sum这些聚合函数不管是写*还是写该非空列都可以走索引扫描,代价远小于全表扫描(扫索引块代价小).如果没有not null约束列,但是该列值没有非空的,而且上面有索引,写sql时添加该列is not null条件效果同上(推荐前一种)。 2 如果有表中有非空约束列(not null)而且上面有索引,或者没有not null约束列,但是该列值没有非空的,而且上面有索引,minmax都可以走索引扫描,代价远小于全表扫描(扫索引块的两端就行)。但是min max同时写更好的写法是select (select min(name) from A) max_val,(select max(name) from A) max_val from dual; |
||
索引全扫描 |
index fast full scan(索引快速扫描和的)和index full scan(索引全扫描)的区别在于:索引快速扫描一次读多个索引块,速度更快,但是无法保证顺序,所以消除不了排序操作(比如不需要排序的count sum avg操作的例子);index full scan一次 读一个索引块,可以保证顺序,可以消除排序操作,减少了逻辑读(比如min max操作的例子)。
|
||
排序 |
注意如果是避免不了的order by语句,优化时可以考虑在该列建索引,可以极大地减少排序,因为索引是有序的。 |
||
主外键 |
如果是主外键约束,可以考虑在外键列加索引,可以提高连接效率(原理和nested loops相同),而且可以避免锁的竞争。 |
||
复合索引 |
一个列上可以同时存在单个索引和复合索引,但重点是前导性和选择性(要把最常出现和最能筛选数据的列放在最前面,前导列出现才走复合索引),复合索引的列不要超过3个。复合索引的其他内容参看上面的SELECT部分。 |
||
位图索引 |
位图索引虽然省空间,对于count(*)效果好,但是致命弱点是怕dml操作,不适合oltp系统。比如男女(或0或1标志位)这样的字段,我们在oltp系统中宁愿不加索引也不用位图索引,因为它dml的加锁特性即使不影响update,也会影响Insert。 |
||
最重要 |
在生产库上大白天建索引而且还parallel 5——oltp不要轻易在白天有业务的时候建索引会死人的有木有,parallel虽然快但是会消耗大量资源有木有,生产库建索引要加online,否则锁整张表不能交易有木有。 |