Hints

Hints是用来约束优化器行为的一种技术。

例如:

  • 约束优化器模式

    • all_rows
    • first_rows
  • 约束访问路径

    • 基于表的数据访问
    • 基于索引的数据访问
  • 约束表关联方式

    • NL:Nested Join
    • MJ:Merge Join
    • HJ:Hash Join

使用范畴:

  • 尽量避免在开发中使用。使用后可能会使得优化器变得死板,不利于CBO数学模型的分析
  • 辅助DBA用来做性能排查和优化

访问路径相关的hints

全表扫描:/*+ full */

  1. -- 强制全表扫描
  2. select /*+ full(t) */ * from t where object_id=10; -- 该语句中加了hints会使查询变差

全表扫描并不一定都是性能差的,比如全表扫描可以使用多数据块读技术。有时候读取的数据占全表很大一部分时,使用多块读的全表扫描比逐一扫描索引可能还会快一些。

索引一般至少需要2次I/O(也可能不止2次,扫描索引的时候,需要先找索引的根,再继续寻找索引的枝叶),一次去索引中查找,然后根据索引去表中找对应行的其他字段。

另外,全表扫描还可以用到并行的技术。

索引相关:/*+ index */ 强制使用索引、/*+ no_index */强制不使用索引

  1. -- 强制使用t表的idx_t索引
  2. select /*+ index(t idx_t) */ * from t where object_id>10; -- 该语句中加了hints会使查询变差
  3. -- 强制不使用索引
  4. select /*+ no_index(t idx_t) */ count(object_id) from t; -- 该语句中加了hints会使查询变差

索引快速扫描(ffs扫描):/*+ index_ffs */

  1. select /*+ index_ffs(t idx_t) */ count(object_id) from t;

索引快速扫描是对索引的多数据块读,通常是对索引字段的统计等操作。

索引跳跃扫描:/*+ index_ss */

  1. select /*+ index_ss(t idx_t) */ count(*) from t where object_id < 100;

索引跳跃扫描是用于替代全表扫描的一种数据访问方法。对于前导重复率高的联合索引,有时候 index skip scan的性能要好一些。(联合索引中,如果sql语句只查询了联合索引的第二个字段,没有查第一个字段,在oracle 9i之前是要走全表扫描的。9i之后加了跳跃扫描,可以跳过前导第一个字段,直接使用第二个字段在索引中查找)

表关联的hints

嵌套关联(Nested Loop Joins):/*+ use_nl */ 从一张表中逐条拿出数据到另一个表里遍历查找

  1. select /*+ use_nl(t, t1) */ t.*
  2. from t, t1
  3. where t.object_id=t1.object_id;

NL的场景:

  • 关联中有一个表比较小(这种场景也适合hash关联)
  • 被关联表的关联字段上有索引
  • 索引的键值不应该重复率很高

Hash Join:/*+ use_hash */

把小表通过hash算法build到内存中,拿大表的数据到小表中比对。

  1. select /*+ use_hash(t t1) */ t.*
  2. from t, t1
  3. where t.object_id=t1.object_id;

Hash Join的应用场景:

  • 一个大表一个小表的关联
  • 表上没有索引
  • 返回结果集比较大

Merge Join:/*+ use_merge */

该算法比较耗费资源。先把两个结果集分别排序,排序之后再做关联。

  1. select /*+ use_merge(t t1) */ *
  2. from t, t1
  3. where t.object_id=t1.object_id;

应用场景:

  • 结果集已经排好序。可以省去merg join再排序的资源。

leading:/*+ leading() */ 规定表连接的顺序

CBO会把关联的所有表都当做驱动表计算一下Cost,寻找最优的一个执行计划。使用leading可以指定哪张表作为驱动表,表连接的顺序。

  1. select /*+ leading(t1 t t2) */ t.*
  2. from t,t1,t2
  3. where t.object_id=t1.object_id
  4. and t1.object_id=t2.object_id;

其他:/*+ append */ 以直接加载的方式插入数据

传统加载方式:Oracle在插入数据时,会优先到该表的高水位以下寻找空闲空间插入数据。

直接加载方式:Oracle出入数据时,直接到高水位以上的空闲空间插入数据,然后将高水位上移。在一定策略下,直接加载方式插入的数据不产生redo。

Archive Mode
是否归档
Object Mode
表模式是否记录日志
Operation Mode Redo
Archivelog LOGGING Append Yes
NO Append Yes
NOLOGGING Append No
NO Append Yes
NoArchivelog LOGGING Append No
NO Append Yes
NOLOGGING Append No
NO Append Yes

动态采样:/*+ dynamic sampling */设置动态采样的级别

  1. select /*+ dynamic sampling(3) */ count(*) from t;

级别越高,动态采样越频繁,精准度越高,但是耗费资源也越高。

并行:/*+ parallel */ 指定并行度

  1. select /*+ parallel(t 2) */ count(*) from t;

分布式:/*+ driving_site() */ 决定一个分布式事务中,操作在哪个节点上完成。

  1. select /*+ driving_site(departments) */ *
  2. from employees, departments@rsite
  3. where employees.department_id = departments.department_id;

如果没有hint,远程表(departments)上的数据将被传到本地来做关联。

使用hint,本地的数据将传到远程节点上执行,最后即将结果返回本地。

cardinality:/*+ cardinality() */ oracle官方文档没有该hint,用来模拟一个结果集的cardinality,不必在表中插入很多数据也可以得出大量数据时表的执行计划

  1. select /*+ cardinality(t 100) */ *
  2. from t, t1
  3. where t.object_id = t1.object_id
  4. and t.object_id > 100;