LATEST TOPICS

ONLINE statistics collection in Oracle Database 12c : Part 1

In my last article “Why Datapump Import (IMPDP) is not ignoring Index Statistics?”, we have seen how the database feature of gathering ONLINE statistics for INDEX prevented IMPDP to exclude the statistics collection during the import process.

Today’s topic of discussion is related to the same area of gathering ONLINE statistics by oracle. However, today we would talk about ONLINE statistics behaviour in Oracle Database 12c.

Oracle had introduced the auto gather (ONLINE) statistics in Oracle version 10g. However, starting with Oracle database 10g, the statistics were collected by default only for the Indexes. In that case whenever an index is created or rebuilt; the statistics for the respective Index would be collected by default without requiring any additional step from DBA.

The same feature of ONLINE statistics collection for Index is extended to Oracle 12c Database as well. Additionally, Starting with Oracle Database 12c, Oracle has extended the ONLINE statistics collection feature to collect statistics for table as well. Starting with Oracle database 12c, whenever a table is created (with CTAS statement) or bulk load is performed in a table (with INSET INTO .. SELECT statement); the statistics are automatically gathered (restrictions applied) for the respective table without the need of any additional manual intervention.

Demonstration

Let me elaborate this feature with a small example for a better understanding.

ONLINE Statistics in Oracle Database 10g or 11g:

Since Oracle 10g and 11g had the same Auto statistics gathering feature, I am using only Oracle 11g database for my demonstration.

Let us create a table with CTAS statement in Oracle 11g database.

SQL> select version from v$instance;

VERSION
-----------------
11.2.0.1.0

SQL> create table test_stat_11g as select * from user_objects;

Table created.

Let check, if the TABLE statistics were collected for this table.

SQL> select TABLE_NAME,NUM_ROWS,SAMPLE_SIZE,LAST_ANALYZED,STALE_STATS from user_tab_statistics where table_name='TEST_STAT_11G';

TABLE_NAME        NUM_ROWS SAMPLE_SIZE LAST_ANAL STA
--------------- ---------- ----------- --------- ---
TEST_STAT_11G

As we can observe, in Oracle database 11g; the statistics were not collected automatically for the table.

Now, let us create an Index on the above table and check if the statistics would be collected automatically for the respective Index.

SQL> create index idx_test_stat_11g on test_stat_11g(object_id);

Index created.

SQL>

SQL> select INDEX_NAME,TABLE_NAME,BLEVEL,LEAF_BLOCKS,NUM_ROWS,SAMPLE_SIZE,LAST_ANALYZED,STALE_STATS from user_ind_statistics where index_name='IDX_TEST_STAT_11G';

INDEX_NAME                     TABLE_NAME          BLEVEL LEAF_BLOCKS   NUM_ROWS SAMPLE_SIZE LAST_ANAL STA
------------------------------ --------------- ---------- ----------- ---------- ----------- --------- ---
IDX_TEST_STAT_11G              TEST_STAT_11G            0           1          2           2 10-MAY-15

Here, we can observe that the statistics for the Index was gathered automatically during the index creation process.

ONLINE Statistics in Oracle Database 12c:

Let us create a table with CTAS statement in Oracle 12c database (the same way that we did for Oracle 11g database).

SQL> select version from v$instance;

VERSION
-----------------
12.1.0.1.0

SQL> create table test_stat_12c as select * from user_objects;

Table created.

Let check, if the table statistics were collected ONLINE for this table in Oracle 12c database.

SQL> select TABLE_NAME,NUM_ROWS,SAMPLE_SIZE,LAST_ANALYZED,STALE_STATS from user_tab_statistics where table_name='TEST_STAT_12C';

TABLE_NAME        NUM_ROWS SAMPLE_SIZE LAST_ANAL STA
--------------- ---------- ----------- --------- ---
TEST_STAT_12C            4           4 10-MAY-15 NO

As we can observe, unlike Oracle 11g database statistics were indeed collected automatically for the table in Oracle 12c database. If we look at the execution plan, there is something new.

-------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------------
|   0 | CREATE TABLE STATEMENT                   |                  |  3450 |  1017K|  2160   (1)| 00:00:01 |
|   1 |  LOAD AS SELECT                          | TEST_STAT_12C    |       |       |            |          |
|   2 |   OPTIMIZER STATISTICS GATHERING         |                  |  3450 |  1017K|  2131   (1)| 00:00:01 |
|   3 |    VIEW                                  | USER_OBJECTS     |  3450 |  1017K|  2131   (1)| 00:00:01 |
|   4 |     UNION-ALL                            |                  |       |       |            |          |
|*  5 |      TABLE ACCESS BY INDEX ROWID         | SUM$             |     1 |    26 |     0   (0)| 00:00:01 |
|*  6 |       INDEX UNIQUE SCAN                  | I_SUM$_1         |     1 |       |     0   (0)| 00:00:01 |
|*  7 |      TABLE ACCESS FULL                   | USER_EDITIONING$ |     1 |     6 |     2   (0)| 00:00:01 |
|   8 |       TABLE ACCESS BY INDEX ROWID BATCHED| OBJ$             |     1 |    30 |     3   (0)| 00:00:01 |
|*  9 |        INDEX RANGE SCAN                  | I_OBJ1           |     1 |       |     2   (0)| 00:00:01 |
|* 10 |      FILTER                              |                  |       |       |            |          |
|* 11 |       HASH JOIN                          |                  |  3631 |   450K|   384   (1)| 00:00:01 |
|  12 |        INDEX FULL SCAN                   | I_USER2          |   124 |  2976 |     1   (0)| 00:00:01 |
|* 13 |        TABLE ACCESS FULL                 | OBJ$             |  3631 |   365K|   383   (1)| 00:00:01 |
|* 14 |       TABLE ACCESS BY INDEX ROWID        | IND$             |     1 |     8 |     2   (0)| 00:00:01 |
|* 15 |        INDEX UNIQUE SCAN                 | I_IND1           |     1 |       |     1   (0)| 00:00:01 |
|* 16 |       TABLE ACCESS FULL                  | USER_EDITIONING$ |     1 |     6 |     2   (0)| 00:00:01 |
|  17 |       NESTED LOOPS SEMI                  |                  |     1 |    29 |     2   (0)| 00:00:01 |
|* 18 |        INDEX SKIP SCAN                   | I_USER2          |     1 |    20 |     1   (0)| 00:00:01 |
|* 19 |        INDEX RANGE SCAN                  | I_OBJ4           |     1 |     9 |     1   (0)| 00:00:01 |
|* 20 |       TABLE ACCESS FULL                  | USER_EDITIONING$ |     1 |     6 |     2   (0)| 00:00:01 |
|  21 |      TABLE ACCESS BY INDEX ROWID BATCHED | LINK$            |     1 |    88 |     0   (0)| 00:00:01 |
|* 22 |       INDEX RANGE SCAN                   | I_LINK1          |     1 |       |     0   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------

OPTIMIZER STATISTICS GATHERING is a new step introduced in the execution plan. This is due to the ONLINE statistics feature being enabled. This step from the execution plan indicates that the optimizer is collecting statistics for the table during the data load process.

Now, let us create an Index on the above table in 12c database and check if the statistics would be collected automatically for the respective Index.

SQL> create index idx_test_stat_12c on test_stat_12c(object_id);

Index created.


SQL> select INDEX_NAME,TABLE_NAME,BLEVEL,LEAF_BLOCKS,NUM_ROWS,SAMPLE_SIZE,LAST_ANALYZED,STALE_STATS from user_ind_statistics where index_name='IDX_TEST_STAT_12C';

INDEX_NAME          TABLE_NAME          BLEVEL LEAF_BLOCKS   NUM_ROWS SAMPLE_SIZE LAST_ANAL STA
------------------- --------------- ---------- ----------- ---------- ----------- --------- ---
IDX_TEST_STAT_12C   TEST_STAT_12C            0           1          4           4 10-MAY-15 NO

Here, statistics were collected for the Index during the index creation process the same way it was done in Oracle 11g database.

How it works

Now, let us see how Oracle actually gathers the statistics for the tables and indexes automatically for us.

Starting from Oracle Database 10g, the ONLINE statistics collection for Index is controlled by the hidden parameter _optimizer_compute_index_stats. The default value for this parameter is TRUE (force index stats collection on index creation/rebuild). If we do not want Oracle to gather ONLINE statistics for indexes,.we can set this parameter to FALSE.

SQL> SELECT
a.ksppinm Param ,
b.ksppstvl SessionVal ,
c.ksppstvl InstanceVal,
a.ksppdesc Descr
FROM
x$ksppi a ,
x$ksppcv b ,
x$ksppsv c
WHERE
a.indx = b.indx AND
a.indx = c.indx AND
a.ksppinm LIKE '/_optimizer_compute_index_stats' escape '/'
/ 

PARAM                          SESSIONVAL      INSTANCEVAL     DESCR
------------------------------ --------------- --------------- ----------------------------------------
_optimizer_compute_index_stats TRUE            TRUE            force index stats collection on index cr
                                                               eation/rebuild

Starting from Oracle database 12c, the ONLINE statistics gathering for tables is controlled by the hidden parameter _optimizer_gather_stats_on_load. The default value for this parameter is TRUE (enable ONLINE statistics gathering). If we do not want Oracle to gather ONLINE statistics for tables, we can set this parameter to FALSE.

SQL> SELECT
a.ksppinm Param ,
b.ksppstvl SessionVal ,
c.ksppstvl InstanceVal,
a.ksppdesc Descr
FROM
x$ksppi a ,
x$ksppcv b ,
x$ksppsv c
WHERE
a.indx = b.indx AND
a.indx = c.indx AND
a.ksppinm LIKE '/_optimizer_gather_stats_on_load' escape'/'
/

PARAM                             SESSIONVAL      INSTANCEVAL     DESCR
--------------------------------- --------------- --------------- -----------------------------------------------
_optimizer_gather_stats_on_load   TRUE            TRUE            enable/disable online statistics gathering

Optionally, we can also use the hint NO_GATHER_OPTIMIZER_STATISTICS in 12c to prevent optimizer from collecting ONLINE statistics for tables participating in bulk loads.

SQL> explain plan for
  2  create table test_stat_12c_1 as
  3  select /*+ NO_GATHER_OPTIMIZER_STATISTICS */  * from dba_objects;

Explained.


SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------
Plan hash value: 1201663749

------------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------------
|   0 | CREATE TABLE STATEMENT                  |                  | 19535 |  7020K| 21585   (1)| 00:00:01 |
|   1 |  LOAD AS SELECT                         | TEST_STAT_12C_1  |       |       |            |          |
|   2 |   VIEW                                  | DBA_OBJECTS      | 19535 |  7020K| 21208   (1)| 00:00:01 |
|   3 |    UNION-ALL                            |                  |       |       |            |          |
|*  4 |     TABLE ACCESS BY INDEX ROWID         | SUM$             |     1 |     7 |     1   (0)| 00:00:01 |
|*  5 |      INDEX UNIQUE SCAN                  | I_SUM$_1         |     1 |       |     0   (0)| 00:00:01 |
|*  6 |     TABLE ACCESS FULL                   | USER_EDITIONING$ |     1 |     6 |     2   (0)| 00:00:01 |
|   7 |      TABLE ACCESS BY INDEX ROWID BATCHED| OBJ$             |     1 |    25 |     3   (0)| 00:00:01 |
|*  8 |       INDEX RANGE SCAN                  | I_OBJ1           |     1 |       |     2   (0)| 00:00:01 |
|*  9 |     FILTER                              |                  |       |       |            |          |
|* 10 |      HASH JOIN                          |                  | 19600 |  2756K|    90   (3)| 00:00:01 |
|  11 |       TABLE ACCESS FULL                 | USER$            |    71 |  1349 |     3   (0)| 00:00:01 |
|* 12 |       HASH JOIN                         |                  | 19600 |  2392K|    87   (3)| 00:00:01 |
|  13 |        INDEX FULL SCAN                  | I_USER2          |    71 |  1704 |     1   (0)| 00:00:01 |
|* 14 |        TABLE ACCESS FULL                | OBJ$             | 19600 |  1933K|    85   (2)| 00:00:01 |
|* 15 |      TABLE ACCESS FULL                  | USER_EDITIONING$ |     1 |     6 |     2   (0)| 00:00:01 |
|  16 |      NESTED LOOPS SEMI                  |                  |     1 |    29 |     2   (0)| 00:00:01 |
|* 17 |       INDEX SKIP SCAN                   | I_USER2          |     1 |    20 |     1   (0)| 00:00:01 |
|* 18 |       INDEX RANGE SCAN                  | I_OBJ4           |     1 |     9 |     1   (0)| 00:00:01 |
|* 19 |      TABLE ACCESS FULL                  | USER_EDITIONING$ |     1 |     6 |     2   (0)| 00:00:01 |
|  20 |     NESTED LOOPS                        |                  |     1 |    43 |     3   (0)| 00:00:01 |
|  21 |      TABLE ACCESS FULL                  | LINK$            |     1 |    24 |     2   (0)| 00:00:01 |
|  22 |      TABLE ACCESS CLUSTER               | USER$            |     1 |    19 |     1   (0)| 00:00:01 |
|* 23 |       INDEX UNIQUE SCAN                 | I_USER#          |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------

As we can see, OPTIMIZER STATISTICS GATHERING step is now disappeared from the execution plan (thanks to our hint to the optimizer).

Conclusion:

The ONLINE statistics collection for tables introduced in Oracle Database 12c is a nice feature indeed. However, there is an additional overhead on the optimizer during the load processing. We need to deal with this feature diligently when there is huge tables participating as it may slow down the optimizer processing.

There are also few limitations associated with this new feature, which I would try to cover in a separate article.

Reference:

https://docs.oracle.com/database/121/TGSQL/tgsql_statscon.htm#TGSQL344

No Responses
%d bloggers like this:
Visit Us On LinkedinVisit Us On TwitterVisit Us On Google PlusCheck Our Feed