INDEX_FFS 能使用前提是所有访问的列都在index中join rowid 实现

APPLIES TO:

Oracle Database - Enterprise Edition - Version 7.3.0.0 to 12.1.0.2 [Release 7.3.0 to 12.1]
Oracle Database Cloud Schema Service - Version N/A and later
Oracle Database Exadata Express Cloud Service - Version N/A and later
Oracle Database Exadata Cloud Machine - Version N/A and later
Oracle Cloud Infrastructure - Database Service - Version N/A and later
Information in this document applies to any platform.

PURPOSE

This article discusses using Index Fast Full Scans (Index FFS) to avoid Full Table Scans (FTS).

SCOPE

Support Analysts and customers who are experiencing problems with their queries doing FTS as opposed to using Index FFS.

DETAILS

When Will Index FFS be used in preference to FTS?

From the Oracle8 Server Concepts manual:
 

  1. The index must contain all the columns referenced in the query.
  2. Index FFS is only available with Cost Based Optimizer (CBO) (Index hint forces CBO).
  3. Index FFS can be hinted with /*+ INDEX_FFS() */ .


Index FFS was introduced in 7.3.
In Oracle7 it requires initialization parameter V733_PLANS_ENABLED to be set to TRUE .

An Index FFS will scan all blocks in the index. The returned data is not sorted.
Index FFS can use multiblock I/O and can be parallelized just like a Full Table Scan.

Examples:

Using the standard emp and dept tables (these can be created with UTLSAMPL.SQL) on Oracle 8.0.5 with no statistics on tables and indexes. The execution plans were generated using autotrace (see Note:43214.1) :

Preparation: Create a composite index

create index emp_ix on emp(empno, deptno, ename);

  • Query of single table, where query selects all columns in index

    SQL> select /*+ INDEX_FFS (emp emp_ix) */ empno, deptno, ename from emp;

    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=21 Bytes=693)
    1 0 INDEX (FAST FULL SCAN) OF 'EMP_IX' (NON-UNIQUE) (Cost=4 Ca
    rd=21 Bytes=693)

  • Query single table, where columns of index are either in select or where clause

    SQL> select /*+ INDEX_FFS (emp emp_ix) */ empno, ename from emp
    where deptno > :bind1;

    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=2 Bytes=66)
    1 0 INDEX (FAST FULL SCAN) OF 'EMP_IX' (NON-UNIQUE) (Cost=4 Ca
    rd=2 Bytes=66)


     
  • Query with all columns of index plus column that is not in the index

    SQL> select /*+ INDEX_FFS (emp emp_ix) */ empno, ename from emp
    where deptno > :bind1 and sal <:bind2;

    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=46)
    1 0 TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=1 Bytes=46)

     

    Note: CBO chooses FTS as INDEX FFS cannot satisfy all columns in query

  • Query involving some of the columns in composite index

    SQL> select /*+ INDEX_FFS (emp emp_ix) */ ename from emp;

    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=21 Bytes=147)
    1 0 INDEX (FAST FULL SCAN) OF 'EMP_IX' (NON-UNIQUE) (Cost=4 Ca
    rd=21 Bytes=147)

     

    NOTE: INDEX FFS still chosen if subset of index columns in query

  • Query involving join

    SQL> select /*+ INDEX_FFS ( e emp_ix) */ e.ename, d.dname
    from emp e , dept d
    where e.deptno=d.deptno;

    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=4 Bytes=168)
    1 0 HASH JOIN (Cost=6 Card=4 Bytes=168)
    2 1 INDEX (FAST FULL SCAN) OF 'EMP_IX' (NON-UNIQUE) (Cost=4
    Card=21 Bytes=420)
    3 1 TABLE ACCESS (FULL) OF 'DEPT' (Cost=1 Card=21 Bytes=462)

Index Fast Full Scan NOT used when using other columns than in the index

gumpx

i'vh currently the  problem hat my table has 170GB (50million rows) size and approx 25 columns

一千万一G前提是10个column附近

i'vh one index (22GB size) with three columns of the table (in this order):

  - processing_status  (not null)----查处理状态吧

  - billing_type (nullable)

  - aggegration_id (nullable)

The final result set is about 20k-50k rows

Because of the low selectivity of all columns in the where clause, I want to use an Fast Full Index Scan to filter the data and then continue to table. The cardinality after index filte should be ok for table access.

index fast full scan不支持第二步回表

So here my Problem:

If I'm goint to use only a count(*) or the three columns in the INDEX i'll get the following plan:

Plan hash value: 1993306444

----------------------------------------------------------------------------------------

| Id  | Operation             | Name           | Rows  | Bytes | Cost (%CPU)| Time     |

----------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT      |                |     1 |     9 |   498K  (2)| 00:00:50 |

|   1 |  SORT AGGREGATE       |                |     1 |     9 |            |          |

|*  2 |   INDEX FAST FULL SCAN| DMS_DATA_AGG_I |    80M|   689M|   498K  (2)| 00:00:50 |

----------------------------------------------------------------------------------------

But when i'm going to add some other columns, it will fall back to Full Table Scan:

Plan hash value: 2483148220

------------------------------------------------------------------------------

| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |

------------------------------------------------------------------------------

|   0 | SELECT STATEMENT  |          |    43M|   662M|  3791K  (2)| 12:38:18 |

|*  1 |  TABLE ACCESS FULL| DMS_DATA |    43M|   662M|  3791K  (2)| 12:38:18 |

------------------------------------------------------------------------------

If i'm trying to force by "/*+ INDEX_FFS(this_ DATA_AGG_I)" hint it will be ignored ...

Is there a special restriction?


Kind Rgds,

Daniel 

-----------------------------

  • Here's a quotation from Note 67522.1:

       CBO can use a Index Fast Full Scan (INDEX_FFS) as long as the index contains all the columns that are needed for the query.

    Therefore, a query against all (indexed )columns does not apply.

     

  • Hi top.gun,

    first thy for the fast reply.

    Hmm i think I will try to explain a little bit more:

    Here an sample sql:

    select *

    from  dms_data b

    where b.processing_status=2

          and not (b.billing_type in (-1, 0, 1))

          and b.aggregation_id is null;

    Normally if the selectivity of one of the where-clause columns will be good a INDEX RANGE SCAN will be the preferred way. 选择性好的话INDEX RANGE SCAN  有效,否则全部都要过一遍index,如上例not (b.billing_type in (-1, 0, 1)) 没有这个not如果返回2w行其实也会是可以的,但是not的话不行

  • But non of the columns are really selectiv by itself. So if i'm just calling process_status i will get 95% of the whole table,same for billing_type and aggregation_id...这里应该不对,index不是一列一列看的,而是多列一起看的,not导致的

  • So the combination of all three column will bring selectivity.如果这样index是可以range scan的 关键是not

  • So far as i see i'm to going to scan always a big part of the index - in case of index range scan by single block read - slow, and in case of fast full scan by mblr(多块读) - fast ...

    I think therefore the fast full index scan would be useful? But can't be used with other colums than the index ones.

    ------

     

  • Hi, Daniel,

    When you choose another column, are you specifying it in the SELECT list, the WHERE clause, or both?

    Is there any difference in behavior, dependent upon whether or not the additional column allows NULLs or not?

    Sincerely,

    -------------

  • The WHERE Clause is complete. So  will just add columns in den SELECT list only. Here an example:

    -- SLOW // Uncached FULL TABLE SCAN

    select *

    from  dms_data b

    where b.processing_status=2

          and not (b.billing_type in (-1, 0, 1))

          and b.aggregation_id is null;

    -- FAST // Uncached FAST FULL INDEX SCAN / Direct Path Read

    select processing_status

    from  dms_data b

    where b.processing_status=2

          and not (b.billing_type in (-1, 0, 1))

          and b.aggregation_id is null;

    No difference between if adding column with null or not null. Both will lead to an Full Table Scan.

     --------select *  决定ffs不能使用

  • You are selecting all columns so a FFIS is not going to happen.

    So if proved the 3 columns together is highly selective, then the index (processing_status, billing_type, aggregation_id) should be a good one.

    Otherwise the FTS may truely be the quickest way. 

  • Hi Ted,

    thy for you information, this may be a feature request :-)

    i'vh tried to build now a workaround sql like, doing a self join:

    select /*+ LEADING(c a) */ a.*

    from dms_data a,

    (select /*+ INDEX_FFS(b DMS_DATA_AGG_I) CARDINALITY(b, 50000) NO_MERGE */ rowid

    from  dms_data b

    where b.processing_status=2

          and not (b.billing_type in (-1, 0, 1))

          and b.aggregation_id is null

    ) c

    where a.rowid = c.rowid;

    Here the plan:

    Plan hash value: 4044183599

    ----------------------------------------------------------------------------------------------

    | Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |

    ----------------------------------------------------------------------------------------------

    |   0 | SELECT STATEMENT            |                | 50000 |    21M|   548K  (2)| 00:00:55 |

    |   1 |  NESTED LOOPS               |                | 50000 |    21M|   548K  (2)| 00:00:55 |

    |   2 |   VIEW                      |                | 50000 |   585K|   498K  (2)| 00:00:50 |

    |*  3 |    INDEX FAST FULL SCAN     | DMS_DATA_AGG_I | 50000 |  1025K|   498K  (2)| 00:00:50 |

    |   4 |   TABLE ACCESS BY USER ROWID| DMS_DATA       |     1 |   439 |     1   (0)| 00:00:01 |

    ----------------------------------------------------------------------------------------------

    It will work, fast too, data seams to be fine. But is that the truth? Is the rowid during an FFS from the table or from the index an so joinable, since the table has not been accessed.

    Here some timings:

    FTS: ~1900s

    FFS on Index Columns ~65s

    FFS combination (example above) ~70s

    RANGE SCAN .. currently working but actual i'm at 2400s increasing ...

    Rgds,

    Daniel

  • ------

  • Clever!

    I wonder whether or not the NOT IN syntax prevents the index from being used.  Do you see an index FFS with this query?

    select *

    from  dms_data b

    where b.processing_status=2

          and b.billing_type in (2,3,4,5)

          and b.aggregation_id is null;-----如果这个返回20k,你们range scan IS better

    Sincerely,

    Ted

  • Hi Ted,

    no the last query also results in an range scan ...

    This maybe because it's not the leading column in the index?

    Rgds,

    Daniel 

  • Feb 12, 2016 6:41PM edited Feb 12, 2016 6:41PM

    Well, there must be a reason that the cost-based optimizer (CBO) does not allow the index fast full scan (FFS).  There probably exists a corner case, though I can't think of any, off the top of my head.  What's telling is that the CBO silently ignored the INDEX_FFS hint, when you specified it, implying it's not possible to use as an access path.

    The use of index FFS, versus a full table scan, is covered in Note 70135.1.  However, it simply repeats what I've already posted in here from Note 67522.1 - that the index must reference all columns listed in the SELECT list.

    Sincerely,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值