SQL optimization Chapter III table access method

Shenkaoziliao:
This series of blog mainly Cenkaoziliao have CUUG database Rannai Gang teacher teaching notes, "SQL optimization core idea" (Luobing Sen, Huang Chao Zhong Jiao with), "PostgreSQL Inside: query optimization Depth" (Zhang Shujie a), ranking in no particular order.

 

By explaining the basis of Chapters I and II, we began to learn how to get data more efficiently from the table, that is, take away the entire table or index, because the index is good, is not a panacea, full table scan, sometimes performance even higher than the index, so-called, even if there is reasonable. At the same time, we will explain the basic theory in different scenarios, various access methods efficient and inefficient reasons.

1 full table scan

select * from emp where sal>1000;

TABLE ACCESS FULL is a full table scan, an asterisk in front of representatives of filter condition.

2 index scan

The only scan the index 2.1

2.2 index range scan

select * from emp where empno>7788;

Unique index scan range scan and index difference with reference to the first chapter <3.1 + tree with a doubly linked list of simple explanation balance index>.

2.3 index skip scan

First, we explain what is the index skip scan

drop table test1;

create table test1 as select * from dba_objects ;

create index ix_test1_01 on test1(owner,OBJECT_ID);

analyze table test1 compute statistics for table for all columns for all indexes;

select  * from test1 t where t.OBJECT_ID =1000;

Simply put, the project is not indexed search conditions leading column, you need to take a different branch when the index scan.

2.4 index fast full scan

这种索引扫描方式主要是为了把胖表(字段很多)表项目提取出来,做一个瘦表,提取数据时,不用从表里提取,直接从索引提取,以减少IO成本。我在项目中用的并不是太多,之索引拿出来专门说一下,是因为这种扫描方式类似于自己构造了一个瘦表的全表扫描。

drop table test1;

create table test1 as select * from dba_objects ;

create index ix_test1_01 on test1(owner,OBJECT_ID);

select   t.OWNER from test1 t where owner = 'SYS';

2.5 有索引但是优化器不用

有时候,我们分明建了索引,但是优化器就是不用,比如以下案例

drop table test1;

create table test1 as select * from dba_objects ;

create index ix_test1_01 on test1(OBJECT_NAME);

select   * from test1 t where TRIM(OBJECT_NAME)='I_IND1';

有些开发者喜欢加个TRIM啥的,初衷是为了增加程序强壮性,避开空格啥的造成程序错误,但是在索引使用中就不要这么用了,我们回顾以下索引结构,

索引里存储的键值对,键是项目值,值是rowid,在检索条件里做了一些处理,或者发生类型转换,哪怕是隐式类型转换,都有可能造成SQL不走索引,如果想用索引,最好保证条件和索引里面存储的完全一致,包括数据类型。

当然,如果就想在条件里用一些函数进行处理,那么可以考虑函数索引,可以自行调查。

 

3 全表扫描和索引扫描选择

通过之前讲解,我们了解了全表扫描和索引扫描,给我们的认识是检索性能索引高于全表扫,索引影响数据增删改性能,那么为什么还保留着全表扫描呢?难不成只是因为应付没有索引的表?答案是错,全表扫在某些场景下性能更高。我们用案例说话。

drop table test1;

create table test1 as select * from dba_objects ;

create index ix_test1_01 on test1(OBJECT_ID);

select * from test1 t where OBJECT_ID <50000;

select  /*+ index(t ix_test1_01)*/ *

from test1 t where OBJECT_ID <50000;

使用索引的逻辑读比全表扫更大,用索引后IO成本增加了,为啥呢?

关键字是TABLE ACCESS BY INDEX ROWID意思是通过索引不能获取所有想要的数据,比如owner字段等,需要用索引定位出来的rowid去表里找数据,就是回表。

这也不能解释逻辑读为啥还增多了。增多的原因是回表是单块读,比如第一次用rowid找到的数据在block1 上,读一次block1,第二次rowid还在block1上,就再读一次,所以逻辑读增加了,全表扫是多块读,就是把数据从头到尾读一遍,那么就不会造成块的重复读入,所以全表扫这个时候逻辑读小了。

那么问题又来了,索引读给做成多块读不就好了。答案是做不到啊(NDEX FAST FULL SCAN是例外,参照第三章 2.4 index fast full scan)

为啥呢,表里数据无序,索引里数据有序,他们的对应关系是交叉的。如下图

(此图片来自CUUG冉乃纲老师教学笔记)

索引读无法实现回表后多块读。因为索引检索出来的数据,极大可能散落在表的任何一个块里。

索引和表的关系,还有一个参数叫聚簇因子,聚簇因子太大,也可能不走索引,原因也是回表的IO成本高。聚簇因子大家可以在网上调查。

那么问题来了,都有好处,都有坏处,我们怎么选择呢?

其实非常简单,走索引后之所以IO成本高是因为回表造成的,回表少,那么问题就不存在了,数据条数返回少,回表不就少了嘛。结论是,返回数量少,那么可以走索引,至于多少是少,要根据实际环境来判定。

反之,返回多就走全表。

 

4 建索引的方法

首先声明,我们这里讨论的是B树索引,位图索引请自行调查。

关于索引使用,之前我们已经有了个结论,返回数据量少,走索引,数据量大,走全表。

那么,我么该怎么建索引呢?

第一个要求,索引字段要出现在where条件里,或者表连接条件(为了应对嵌套循环连接,后面第五章 表连接方法 讲解)里,(部分情景出现在order by等其他条件里,为了利用索引有序性,使用场景有限,个别调查验证吧,我们不详细说明了)。

第二个要求,表字段重复值要少,也就是去重复后还有很多数据,并且分布均匀,具体可以网上调查<基数><选择性>。

比如dba_objects的object_id字段很适合建立索引,而owner建索引有时候反而引发性能问题,重复值少,分布均匀,那么用索引检索的数据就会少,回表少,减少IO;重复值多,索引检索的数据也多,回表就多;数据不均匀,倾斜大,同时会伴随重复数据多,比如dba_objects的owner=‘SYS’的数据,可能在嵌套循环时引发数据大量翻倍,具体在第六章 查询转换<半连接和内连接转换>有案例讲解。

始于索引的分类,函数索引,反向键索引,组合索引等,希望大家自行调查。

Guess you like

Origin blog.csdn.net/songjian1104/article/details/91349929