索引中字段顺序对访问性能影响(四)

索引中字段的顺序确实会对访问性能有所影响,不过并不像很多人想象的那么简单。

这一篇讨论选项性低的列作为前缀索引时执行范围扫描的情况。

索引中字段顺序对访问性能影响(一):http://yangtingkun.itpub.net/post/468/509188

索引中字段顺序对访问性能影响(二):http://yangtingkun.itpub.net/post/468/509265

索引中字段顺序对访问性能影响(三):http://yangtingkun.itpub.net/post/468/509328

 

 

上一篇文章将选择性高的字段作为索引的前缀字段,这篇讨论选择性低的字段作为索引前缀的情况。

SQL> drop index ind_t_id_owner;

Index dropped.

SQL> create index ind_t_owner_id
  2  on t (owner, object_id);

Index created.

SQL> select object_name
  2  from t
  3  where object_id > 45000
  4  and wner = 'U1';

OBJECT_NAME
--------------------------------
T_BIG
S1
T


Execution Plan
----------------------------------------------------------
Plan hash value: 2014292028

--------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |     1 |    35 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T              |     1 |    35 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IND_T_OWNER_ID |     1 |       |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OWNER"='U1' AND "OBJECT_ID">45000 AND "OBJECT_ID" IS NOT NULL)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          6  consistent gets
          0  physical reads
          0  redo size
        583  bytes sent via SQL*Net to client
        492  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          3  rows processed

如果是后缀字段的范围扫描,那么上一篇文章介绍的情况没有什么区别。只不过对于选择性不高的索引而言,由于返回的结果相对较多,因此产生的逻辑读也会较大。

SQL> select object_name
  2  from t
  3  where owner > 'S'
  4  and object_id = 50000;

OBJECT_NAME
------------------------------------------
MGMT_CURRENT_METRIC_ERRORS


Execution Plan
----------------------------------------------------------
Plan hash value: 2801825686

--------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |     1 |    35 |    25   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T              |     1 |    35 |    25   (0)| 00:00:01 |
|*  2 |   INDEX SKIP SCAN           | IND_T_OWNER_ID |     1 |       |    24   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OWNER">'S' AND "OBJECT_ID"=50000 AND "OWNER" IS NOT NULL)
       filter("OBJECT_ID"=50000)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         16  consistent gets
          0  physical reads
          0  redo size
        542  bytes sent via SQL*Net to client
        492  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

当选择性差的索引作为前缀字段,且执行范围扫描,而后缀选择性好的字段进行等值扫描时,执行计划选择了索引跳扫。这就与上一篇文章描述的情况有所区别了。

当前缀字段的DISTINCT值不是很多的情况下,INDEX SKIP SCAN扫描成为可能,这种情况下,Oracle扫描前缀字段每种可能性对应的后缀字段的值。

观察Predicate Information可以发现,OWNER > ‘S’这个前缀条件也出现在ACCESS条件中。这说明什么问题呢,为了更好的说明这个问题,修改一下SQL语句:

SQL> select object_name
  2  from t
  3  where owner || '' > 'S'
  4  and object_id = 50000;

OBJECT_NAME
----------------------------
MGMT_CURRENT_METRIC_ERRORS


Execution Plan
----------------------------------------------------------
Plan hash value: 2801825686

--------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |     1 |    35 |    60   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T              |     1 |    35 |    60   (0)| 00:00:01 |
|*  2 |   INDEX SKIP SCAN           | IND_T_OWNER_ID |     1 |       |    59   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=50000)
       filter("OBJECT_ID"=50000 AND "OWNER"||''>'S')


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         37  consistent gets
          0  physical reads
          0  redo size
        542  bytes sent via SQL*Net to client
        492  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

执行计划没有改变,但是逻辑读却相差很多,原因仍然是ACCESSFILTER导致的。在前一个语句中,前缀字段的限制条件出现在ACCESS中,说明在执行索引跳扫的时候,会根据前缀索引的条件来确定扫描的范围,因此索引跳扫只扫描前缀字段大于’S’,且不为空的可能性上。而对于而一个SQL语句,前缀字段的查询条件出现在FILTER处,因此索引跳扫需要访问前缀字段的每一种的可能性,产生的逻辑读当然要大得多。

SQL> select object_name
  2  from t
  3  where owner > 'S'
  4  and object_id in (50000, 50001);

OBJECT_NAME
-----------------------------------------------------
MGMT_CURRENT_METRIC_ERRORS
MGMT_TARGET_ASSOC


Execution Plan
----------------------------------------------------------
Plan hash value: 2014292028

--------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |     1 |    35 |    49   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T              |     1 |    35 |    49   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IND_T_OWNER_ID |     1 |       |    48   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OWNER">'S' AND "OWNER" IS NOT NULL)
       filter("OBJECT_ID"=50000 OR "OBJECT_ID"=50001)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         48  consistent gets
          0  physical reads
          0  redo size
        612  bytes sent via SQL*Net to client
        492  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed

最后看一下前缀字段范围扫描,后缀字段不再是简单相等的情况。这时执行计划与上一篇文章中介绍的情况又恢复了一致。原因是无论后缀字段是IN语句还是范围查询,都使得索引跳扫执行计划不可能成立。显然Oracle不会生成INLIST INDEX SKIP SCAN这种复杂的执行计划。

这时只有使用选择性不高的前缀字段的范围扫描,因此产生的逻辑读显然高得多。

如果将语句修改一下,变成两个UNION ALL的子查询,则语句仍然可以利用索引跳扫:

SQL> select object_name
  2  from t
  3  where owner > 'S'
  4  and object_id = 50000
  5  union all
  6  select object_name
  7  from t
  8  where owner > 'S'
  9  and object_id = 50001;

OBJECT_NAME
------------------------------
MGMT_CURRENT_METRIC_ERRORS
MGMT_TARGET_ASSOC


Execution Plan
----------------------------------------------------------
Plan hash value: 2279076093

--------------------------------------------------------------------------------------------
| Id| Operation                    | Name           | Rows | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|  0| SELECT STATEMENT             |                |    2 |    70 |    51  (51)| 00:00:01 |
|  1|  UNION-ALL                   |                |      |       |            |          |
|  2|   TABLE ACCESS BY INDEX ROWID| T              |    1 |    35 |    25   (0)| 00:00:01 |
|* 3|    INDEX SKIP SCAN           | IND_T_OWNER_ID |    1 |       |    24   (0)| 00:00:01 |
|  4|   TABLE ACCESS BY INDEX ROWID| T              |    1 |    35 |    25   (0)| 00:00:01 |
|* 5|    INDEX SKIP SCAN           | IND_T_OWNER_ID |    1 |       |    24   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("OWNER">'S' AND "OBJECT_ID"=50000 AND "OWNER" IS NOT NULL)
       filter("OBJECT_ID"=50000)
   5 - access("OWNER">'S' AND "OBJECT_ID"=50001 AND "OWNER" IS NOT NULL)
       filter("OBJECT_ID"=50001)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         30  consistent gets
          0  physical reads
          0  redo size
        612  bytes sent via SQL*Net to client
        492  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed

就像文章开头说的,索引字段顺序对于访问性能的影响并不像想象中的那么简单,事实上问题的复杂性也超出我的预期。本来打算通过两篇文章描述一下这个问题,后来发现两篇文章说不清楚,等到第三篇的时候发现,如果还仿照前两篇的架构来说明文章,恐怕很容易乱,于是就又分了两篇文章。

这篇文章并没有打算说明选择性高字段作为索引前缀好,还是选择性的字段作为索引前缀好。和很多特性一样,如何做选择要看在什么情况下使用。

 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/4227/viewspace-680315/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/4227/viewspace-680315/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值