在索引块中,既存储每个索引的键值,也存储具有该键值的行的ROWID。所以索引扫描其实分为两步:
Ⅰ:扫描索引得到对应的ROWID
Ⅱ:通过ROWID定位到具体的行读取数据
Oracle中的优化器是SQL分析和执行的优化工具,它负责生成、制定SQL的执行计划。
Oracle的优化器有两种:
· RBO(Rule-Based Optimization)基于规则的优化器
· CBO(Cost-Based Optimization)基于代价的优化器
RBO:
RBO有严格的使用规则,只要按照这套规则去写SQL语句,无论数据表中的内容怎样,也不会影响到你的执行计划;
换句话说,RBO对数据“不敏感”,它要求SQL编写人员必须要了解各项细则;
RBO一直沿用至ORACLE 9i,从ORACLE 10g开始,RBO已经彻底被抛弃。
CBO:
CBO是一种比RBO更加合理、可靠的优化器,在ORACLE 10g中完全取代RBO;
CBO通过计算各种可能的执行计划的“代价”,即COST,从中选用COST最低的执行方案作为实际运行方案;
它依赖数据库对象的统计信息,统计信息的准确与否会影响CBO做出最优的选择,也就是对数据“敏感”。
1) 运用HASH算法,得到一个HASH值,这个值可以通过V$SQLAREA.HASH_VALUE查看
2) 到sharedpool 中的 library cache 中查找是否有相同的HASH值,如果存在,则无需硬解析,进行软解析
3) 如果sharedpool不存在此HASH值,则进行语法检查,查看是否有语法错误
4) 如果没有语法错误,就进行语义检查,检查该SQL引用的对象是否存在,该用户是否具有访问该对象的权限
5) 如果没有语义错误,对该SQL进行解析,生成解析树,执行计划
6) 生成ORACLE能运行的二进制代码,运行该代码并且返回结果给用户
nested loop(嵌套循环):关联条件的列要有索引,驱动表的数据远小于被驱动表
特点:
1.一个大表和一个小表(驱动表)连接,连接方式可以是等值或者是不等值
2.驱动表数据较小或者内部表已连接的列有唯一性索引或者高度可选的非唯一性索引,效率很高
特点:
1.一般两张相同大小的表连接,初始参数hash_join_enable=true
2.只能是等价连接,只能是CBO模式
3.只有一张源表需要排序,可能比merge join更快,因为只需要对一张源表排序;
也可能比nestedloop更快,因为处理内存中的hash表比处理b-tree索引更快
4.可能会使用到临时表空间,所以最好pag_aggregate_target设置的比较大
hash join(散列连接)(只适合出现在等值连接的情况下,哈希连接比较适用于返回大数据量结果集的连接。
特点:
1.首先对2张表的连接列进行排序后再连接
2.当缺乏数据选择性或者有效索引时,或者2个表都比较庞大,可能比nested loop更有效
将瓶颈sql块单独取出,查看其执行计划,
首先检查其中使用了全表扫描的对象,判断其是否合适。引发错误使用的情形通常是:
1) 没有建立合适的索引列导致全表扫描
2) 非函数索引列使用了函数引发全表扫描
3) 对象统计信息过旧或未收集导致全表扫描、
4) 对于位图索引,直方图信息的缺失有时也会导致错误的全表扫描
然后检查其中无用的表,确认无用的表可以直接去掉减少访问步骤减小系统开销。
接着检查其中是否有重复访问的表,查看是否可以减少访问次数一般可以通过使用with或者建立中间表来优化。
优化访问路径,数据库优化器也有不那么聪明的时候,有时候它生成的访问路径可能并不是最优的,可尝试使用hint来改变访问路径进行优化
执行计划的两个字段:
access_predicates【访问条件】:sql语句中,确定如何在当前步骤中提取记录的子句。它可以包含提供给索引检索或表连接的子句。在这块把数据给过滤掉,一般会用到索引
filter_predicates【过滤条件】:sql语句中确定如何对记录进行过滤的子句,如where子句在非索引列上的条件。一般不用索引,都是全表扫描,可以作为优化重点关注的地方!
设置自动收集统计信息的开启与关闭:
Begin
Dbms_scheduler.enable(‘GATHER_STATS_JOB’);
END;
Begin
Dbms_scheduler.disable(‘GATHER_STATS_JOB’);
END;
动态采样(DynamicSampling)是在ORACLE9i Release 2中开始引入的一个技术,引入它的目的是为了应对数据库对象没有分析(统计信息缺失)的情况下,优化器生成更好的执行计划。简单的说,在数据库段(表、索引、分区)对象没有分析的情况下,为了使CBO优化器得到足够多的信息以保证优化器做出正确执行计划而发明的一种技术。它会分析一定数量段对象上的数据块获取CBO需要的统计信息。动态采样技术仅仅是统计信息的一种补充,它不能完全替代统计信息分析。
有时候即使为对象收集了统计信息但是执行计划还是不对,此时用动态采样再处理一下。
分区表类型如下:
分区表类型 |
简述 |
RANGE(范围)分区 |
例如查询某个月的数据 |
LIST(列表)分区 |
例如查询某个地区的数据 |
HASH(哈希)分区 |
数据分配到每个分区的量是均衡的 |
组合分区 |
分为(range-list,range-hash) |
索引:
B树索引:目前数据库中最常用的索引,构造类似于二叉树,能根据键值提供一行或一个行集的快速访问,其中的’B’代表平衡, 通常使用在频繁使用查询谓词的列上,一般这类列的选择度都较高。
1、通过索引访问表中的数据占比越少越有效
2、如果能使用索引列回答问题(只用到索引列不用访问表)那么返回数据占比很大索引也是有效的
3、数据的物理组织有时未按照索引列或主键列有序的填充表,会影响索引的使用
4、空值会影响索引的使用,在有空值的列上通过与虚拟列建立组合索引,可以使优化器选择索引。而且索引的大小并没有明显变化
在B树索引中,索引键值与行之间存在一种一对一的关系,一个索引键值引向一行,而在位图索引中,一个索引键值则对应多行,位图索引通常适用于高度重复(相对于很多的行数,列值可能只有几个,列值/行数越接近0则越适合使用位图索引)而且经常只读的列,通常查询这种列返回的数据占比很大,因此也不适合使用B树索引。对比来看,B树索引通常是选择性的,位图索引位通常不是选择性的。位图索引的键值使用0,1存储,相较B树索引节省很大的空间另外位图索引可以存储NULL值。
1、一个查询条件包含多个列,并且要创建索引的列只有几个不同的值及大量的聚合统计查询(where条件中使用and/or/in)
1. 位图索引使用于低基数的列(比如说性别列,数据仓库中的维表的主键),相对于B树索引,它的count,and,or操作更有效
2. 位图索引存放的是0,1的比特位,相对于B树索引,占字节数特别少
使用位图索引要特别注意
1. 列的基数比较多,不适合位图索引,因为它会占用更多的存储空间
2.索引列DML频繁的列,不适合位图索引,容易造成死锁,原因是一个位图索引键值指向多行,如果一个会话修改了一行数据,大多数情况下这个键值所对应的所有行都会被锁定。大大影响到系统并发性。数据仓库项目中对于位图索引的维护一般建议先删掉索引加载完完数据后再建立索引
3.关于列偏态或称列倾斜、倾斜列对使用索引的影响,这种列的特点是数据大多集中在某几个值。这种情况下一般会影响索引的使用,通常情况下需要收集表的直方图信息来使优化器决定是否使用索引。
除以上总结的优化方法外在本轮优化中还尝试使用了其他方法,主要如下:
l 定位脚本中的sql 瓶颈:使用pl/sql developer测试窗口中的profiler来定位是哪一段sql耗时较久
l 去掉orderby,排序是一项开销很大的操作,如果非必须可以去掉(需要考虑B树索引中的簇族因子,如果列的排序过于无序也会导致索引的失效参看B树索引部分,这是一个需要平衡的选择)
l 将leftjoin 改写成子查询,根据子查询的特性对于返回到外层查询的记录来说,子查询会每次执行一次。因此,必须保证任何可能的时候子查询都要使用索引,如果父查询只返回较少的记录,那么再次执行子查询的开销不会非常大。因此在连接左侧的表如果在执行计划中使用了全表扫描并且没有参与where的条件可以考虑转换成关联子查询。
l 使用语句级并行,用资源换时间多数使用在全表扫描情况下。
l 增加过滤条件,可能会更改业务逻辑需要慎重选择。
l 一段sql中对一张大表进行了全表查询部分列,这种情况,需要使用全量抽取改增量抽取的方式来优化
l 2. 能看懂数据库的执行计划。
l 3. 了解多表关联的几种方式(Nest Loop,hashjoin,mergeinto…)
l 4. 常见Hint的使用
l 5. 了解常见的几种索引类型及特性,已经执行计划对索引的选择是否正确,比如全索引扫描,跳跃扫描等。
l 6. 永远都要确保对象上的统计信息是准确的,这个是CBO 来评估执行计划的重要参考,只要执行计划正确,索引合适,这条SQL 的性能不会差到哪里。
l 7. 当遇到某条SQL 异常导致数据库出现性能问题,比如CPU 100%的时候,需要知道判断SQL执行计划是否有变化,并知道如何利用SQLprofile等来固定正确的执行计划。