Thursday, March 23, 2017

12.2 move tables online

One of the really cool new features introduced in Oracle 12c release 2 is the capability to move tables ONLINE, while maintain all the associated index structures. This was possible for table partitions in Oracle 12c release 1, but only for index organized tables before then.
I will start by creating a table and specifically populate a CODE column with 100 distinct values that are distributed throughout the entire table structure.
demo@ORA12C> create table t as
  2  select rownum as id ,
  3     mod(rownum,100) as code,
  4     sysdate - mod(rownum,1000) as created,
  5     object_name
  6  from all_objects ,
  7      all_users
  8  where rownum <= 2000000;
 
Table created.
 
demo@ORA12C>
 
If we create an index on the CODE column, we will see the index has a terrible clustering factor, as the values basically appear throughout the entire table structure in each of the table blocks.
 
demo@ORA12C> create index t_idx on t(code) nologging;
 
Index created.
 
demo@ORA12C> select i.index_name, i.clustering_factor,i.status,
  2         t.num_rows, t.blocks
  3  from user_tables t ,
  4    user_indexes i
  5  where t.table_name = i.table_name
  6  and t.table_name ='T';
 
INDEX_NAME    CLUSTERING_FACTOR STATUS     NUM_ROWS     BLOCKS
------------- ----------------- -------- ---------- ----------
T_IDX                   1583789 VALID       2000000      16663
 
 
If we now run a query that returns rows for just one CODE values (i.e. just 1% of the table).
 
demo@ORA12C> set autotrace traceonly explain statistics
demo@ORA12C> select * from t where code = 42;
 
20000 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 20000 |  1054K|  4542   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    | 20000 |  1054K|  4542   (1)| 00:00:01 |
--------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("CODE"=42)
 
 
Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
      16636  consistent gets
      16492  physical reads
          0  redo size
    1153360  bytes sent via SQL*Net to client
       2071  bytes received via SQL*Net from client
        135  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      20000  rows processed
 
demo@ORA12C> set autotrace off
 
We can see the CBO ignores the index and opts for a full table scan, with such a terrible clustering factor and with each block in the table having to be accessed; the CBO is making the correct decision here. The index is effectively useless, even though we are only interested in just 1% of the table.
 
If this query was important to us and executed frequently, we might want to look at improving the clustering factor and hence the efficiency of this index. So reorganize the table such that data in the table is sorted / clustered in CODE order.
 
Oracle 12c release 1 (12.1.0.2) introduced the concept of table attribute clustering, by which table after a reorg or bulk load will store the data based on this clustering attribute.
 
demo@ORA12C> alter table t add clustering by linear order(code);
 
Table altered.
 
demo@ORA12C>
 
This now ensures that data within the table will be in CODE order after a table re-org, but we need to reorg this table for this to take effect. However prior to 12.2 it wasn’t possible to subsequently MOVE the table ONLINE.
 
Online Table move is not possible prior to 12.2 (moving a table online in 11g (11.2.0.4) database, produces this error)
 
demo@ORA11G> alter table t move ONLINE;
alter table t move ONLINE
*
ERROR at line 1:
ORA-01735: invalid ALTER TABLE option
 
Moving a table previously caused locking issues and resulted in all associated index being unusable and having to rebuilt, further adding to the effective unavailability of the table.
 
demo@ORA11G> alter table t move ;
alter table t move
            *
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
 
 
demo@ORA11G> alter table t move ;
 
Table altered.
 
demo@ORA11G> select i.index_name, i.clustering_factor,i.status,
  2         t.num_rows, t.blocks
  3  from user_tables t ,
  4    user_indexes i
  5  where t.table_name = i.table_name
  6  and t.table_name ='T';
 
INDEX_NAME    CLUSTERING_FACTOR STATUS     NUM_ROWS     BLOCKS
------------- ----------------- -------- ---------- ----------
T_IDX                   1301875 UNUSABLE    2000000      13174
 
demo@ORA11G>
 
This now got changes in 12c release 2, where we can now move tables online while fully maintaining all associated indexes.
 
demo@ORA12C> alter table t move ONLINE;
 
Table altered.
 
demo@ORA12C> select i.index_name, i.clustering_factor,i.status,
  2         t.num_rows, t.blocks
  3  from user_tables t ,
  4    user_indexes i
  5  where t.table_name = i.table_name
  6  and t.table_name ='T';
 
INDEX_NAME     CLUSTERING_FACTOR STATUS     NUM_ROWS     BLOCKS
-------------- ----------------- -------- ---------- ----------
T_IDX                      16491 VALID       2000000      16663
 
 
After the table re-org, we note that not only is the index still a valid state, but because the table re-org effectively sorted the data in CODE order due to the attribute clustering, it has resulted in much more reduced clustering factor (just 16,491 reduced down from 1,583,789)
 
If we re-run our original query:
 
demo@ORA12C> select * from t where code = 42;
 
20000 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 767293772
 
---------------------------------------------------------------------------------------------
| Id  | Operation                           | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |       | 20000 |  1054K|   206   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T     | 20000 |  1054K|   206   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | T_IDX | 20000 |       |    41   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("CODE"=42)
 
 
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        476  consistent gets
          0  physical reads
          0  redo size
    1214141  bytes sent via SQL*Net to client
       2071  bytes received via SQL*Net from client
        135  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      20000  rows processed
 
demo@ORA12C>
 
We noticed that index is now used and has resulted in a significant reduction of consistent gets (just 476 down from 16636), the query is much more efficient than it was previously.

No comments:

Post a Comment