SQL调优之十:访问路径(Access Path)一
简介
访问路径是指查询过程获取数据的方式,比如说全表扫描,亦或是索引范围扫描。
访问路径介绍
访问路径每一次只对应一个数据集,这个跟连接过程需要两个数据集是不一样的。
对于不同的关系型数据结构,数据库会采用不同的访问路径:
访问路径 | 堆组织表(无索引) | B树索引和索引组织表 | 位图索引 | 表聚簇 |
Full Table Scans | y | |||
Table Access By Rowid | y | |||
Sample Table Scans | y | |||
Index Unique Scans | y | |||
Index Range Scans | y | |||
Index Full Scans | y | |||
Index Fast Full Scans | y | |||
Index Skip Scans | y | |||
Index Join Scans | y | |||
Bitmap Index Single Value | y | |||
Bitmap Index Range Scans | y | |||
Bitmap Merge | y | |||
Cluster Scans | y | |||
Hash Scans | y |
一般来说,当要获取一个数据集里面的一小部分的时候,使用索引的访问路径的效率要高一点。相反的,如果要获取的是数据集的大部分内容,则全表扫描可能会更好。
表的访问路径
表是oracle数据库中数据组织的基本单元。
关系型表是最常见的表类型:
- 堆组织表,行数据不以特定顺序存储
- 索引组织表,数据按主键值排序
- 外部表,只读表,表的元数据存储在数据库内部,但数据存储在数据的外部
接下来的内容主要讲的是关于堆组织表的访问
关于堆组织表的访问
一般来说,我们创建的表默认是堆组织表。每次用户往里面插入数据的时候,数据库会把它们放在找到的第一个可用的空闲空间,因此数据的存放和获取顺序都是随机的。
数据块和段中的行存储
数据库将数据存储在数据块(block)中。在表里面,数据库把行数据写到数据块的底部。然后使用数据块的头部来管理数据块本身,比如说行路径和表路径。
分区(extent)是由逻辑连续的数据块组成的,这些数据库在物理上可能不是连续的。
段(segment)则是一个表空间里面一个逻辑存储结构,比如说表。它可以由多个分区组成。同样的,索引也是一个逻辑存储结构,它也构成了一个段,可以由一个或多个分区组成。
默认的,对于普通的,本地管理的表空间,数据库使用自动段空间管理(ASSM)。
当一个会话第一次往一个表里面插入数据的时候,数据库会格式化一个位图块。位图块会追踪段里面的数据块。
数据库使用这一位图来查找空闲块,然后在往数据块里面写数据之前对其进行格式化。
ASSM会在多个数据块之间展开插入操作来避免并发性问题。
高水位是一个段里面标志了哪些数据块没有格式化过并且从来没有被使用过。
在高水位以下的数据块,可能是已经格式化并且写入了数据的,格式化了但还是空的,或者没有格式化的。低高水位(low high water mark)以下则表示哪些数据块是已经被格式化了,表示它们当前有数据,或者之前有数据。
在一次全表扫描的过程中,数据库会读取低位高水位以下的所有数据块,然后再去读取段的位图,来判断在HWM和low HWM之间有哪些格式化了的数据块可以被读取。
数据库不会去读HWM以上的数据块,毕竟还没格式化。
ROWID对于访问路径的重要性
堆表的每一行都有一个唯一的rowid指向该行的物理地址。rowid是行的一个10byte的物理地址。
它指向一个特定的文件,块和行编号。
比如说,AAAPecAAFAAAABSAAA,最后的AAA表示行编号。它可以用来索引一个行路径记录。
而行路径记录里面则包含了指向行在数据块中的位置的指针。
数据库有些时候可以移动在数据块底部的行数据。
比如说,如果启用了row movement,那么当更新分区键,flashback table操作,shrink table操作的时候,行就可以被移动。
如果数据库只是在数据块内部移动行,那么数据库会更新行路径记录来修改指针。rowid保持不变。
oracle数据库内部使用rowid作为索引的结构。
比如说,B树索引的每一个键都会关联一个rowid,来指向相关行的地址。
物理rowid提供了最快访问表行的可能性,允许数据库通过单次I/O来获得行数据。
物理rowid存储了堆表、表聚簇、表分区和索引分区的行的地址
逻辑rowid存储的是索引组织表的行的地址