Introduction In my last article, I had discussed about the Oracle 12c new feature In Database Archival . We had explored this new Oracle database feature and how it can be used to archive data within the same database table. We had also familiarized ourselves with the methods available to query and restore the archived data. In the previous article , we have seen how we can archive data within the same table by means of a new table clause ROW ARCHIVAL . We have also seen, once we set a table for row archival, a new table column with name ORA_ARCHIVE_STATE is introduced by Oracle and is used to control whether a particular record (row) within the table is archived or not. A value of 0 for the column ORA_ARCHIVE_STATE indicates the record is ACTIVE and a non-zero value indicates the record being ARCHIVED and by default all the records are in ACTIVE state. Today's article is primarily focused on optimizing the utilization of In Database Archival. In today's article, I would be discussing two important aspects of this new Oracle Database 12c archival feature. The first discussion emphasizes on optimizing the space utilization for the archived data and the second discussion is related to optimizing the query performance when querying the table enabled with row archival. Space optimization for In Database Archiving With "In Database Archival", the data is archived within the same table. It is physically present in the same database and just the logical representation is altered at the query (optimizer) level when we query the data, by means of the control column ORA_ARCHIVE_STATE . This means, the archived data still occupies the same amount of space (unless compressed) within the database. Now, consider if the table is on a Tier - 1 storage device; we are wasting a substantial amount of cost just to maintain the archived data. Wouldn't it be great, if we can store those archived records on a lower level storage device and able to compress those records to further cut down the cost involved in the space allocation. Guess what! this is possible with "In Database Archival" as it provides an option to optimize the space utilization by allowing us partition the table records based on its state. This means we can partition a table on the control column ORA_ARCHIVE_STATE to direct the archived data to be stored on a different storage unit (tablespace), which also enables us to apply compression just on the archived data to further trim down the space utilization for the archived data. Lets quickly go through a simple demonstration to understand these abilities. Demonstration Assumptions: Tablespace APPDATA is located on a Tier-1 storage Tablespace ARCHDATA is located on a Tier-2 storage Goal: I would like to create a table TEST_DATA_ARCH_PART with ROW ARCHIVAL being enabled. I would want the ACTIVE data to be stored on Tier-1 storage in a NOCOMPRESS format and the ARCHIVED data to be stored on Tier-2 storage in COMPRESSED format. This is to ensure that, we are utilizing the database space to its optimal level. Lets create our table TEST_DATA_ARCH_PART with ROW ARCHIVAL enabled. ----// ----// Creating table with ROW ARCHIVAL //---- ----// SQL> create table TEST_DATA_ARCH_PART 2 ( 3 id number, 4 name varchar(15), 5 join_date date 6 ) 7 ROW ARCHIVAL 8 partition by list (ORA_ARCHIVE_STATE) ---// partitioned on record state //--- 9 ( 10 partition P_ACTIVE values(0) tablespace APPDATA, ---// ACTIVE records //--- 11 partition P_ARCHIVED values(default) tablespace ARCHDATA ROW STORE COMPRESS ADVANCED ---// ARCHIVED records //--- 12 ); Table created. ----// ----// Defining primary key for the table //---- ----// SQL> alter table TEST_DATA_ARCH_PART add constraint PK_TEST_DATA_ARCH_PART primary key (ID); Table altered. In the above example, we have created the table TEST_DATA_ARCH_PART with ROW ARCHIVAL enabled. We have partitioned the table on the record state (ORA_ARCHIVE_STATE) to store the ACTIVE data (P_ACTIVE) on Tier-1 storage (APPDATA) and the ARCHIVED data (P_ARCHIVED) on Tier-2 storage (ARCHDATA). We have further enabled COMPRESSION to be applied on all the ARCHIVED records. Let's populate our table with some data. ----// ----// populating table with data //---- ----/ / SQL> insert /*+ APPEND */ into TEST_DATA_ARCH_PART 2 select rownum, rpad('X',15,'X'), sysdate 3 from dual connect by rownum commit; Commit complete. We have populated our table with 1000000 records and all the records are in ACTIVE state by default. We can validate that by querying the table as follows. ----// ----// validating table records //---- ----// SQL> select count(*) from TEST_DATA_ARCH_PART; COUNT(*) ---------- 1000000 SQL> select count(*) from TEST_DATA_ARCH_PART partition (p_active); COUNT(*) ---------- 1000000 SQL> select count(*) from TEST_DATA_ARCH_PART partition (p_archived); COUNT(*) ---------- 0 ----// ----// validating active records are on Tier-1 storage device (APPDATA) //---- ----// SQL> select owner,segment_name as "Table Name",tablespace_name,sum(bytes)/1024/1024 Size_MB 2 from dba_segments where segment_name='TEST_DATA_ARCH_PART' group by owner, segment_name,tablespace_name; OWNER Table Name TABLESPACE_NAME SIZE_MB ------------- ------------------------- -------------------- ---------- MYAPP TEST_DATA_ARCH_PART APPDATA 40 As we can see, all of our table records are in ACTIVE state and thus stored on the Tier-1 storage device (APPDATA). Lets archive some records from our table as shown below. ----// ----// archive records by setting ORA_ARCHIVE_STATE to 1 //---- ----// SQL> update TEST_DATA_ARCH_PART 2 set ORA_ARCHIVE_STATE=1 where id select table_name,row_movement from dba_tables where table_name='TEST_DATA_ARCH_PART'; TABLE_NAME ROW_MOVE ------------------------- -------- TEST_DATA_ARCH_PART DISABLED ----// ----// Enabling row movement for table //---- ----// SQL> alter table TEST_DATA_ARCH_PART enable row movement; Table altered. SQL> select table_name,row_movement from dba_tables where table_name='TEST_DATA_ARCH_PART'; TABLE_NAME ROW_MOVE ------------------------- -------- TEST_DATA_ARCH_PART ENABLED Let's try again to archive the table records by setting the control column ORA_ARCHIVE_STATE to value 1 as shown below. ----// ----// archiving all table records by setting ORA_ARCHIVE_STATE to 1 //---- ----// SQL> update TEST_DATA_ARCH_PART 2 set ORA_ARCHIVE_STATE=1; 1000000 rows updated. SQL> commit; Commit complete. As expected, we are now allowed to ARCHIVE the table records. The archived records are stored in a lower level storage tier by means of tablespace ARCHDATA and are compressed to further trim down the space utilization for archived data. We can validate this fact as shown below. ----// ----// No active records present in the table //---- ----// SQL> select count(*) from TEST_DATA_ARCH_PART; COUNT(*) ---------- 0 ----// ----// Enable archive record visibility //---- ----// SQL> alter session set row archival visibility=all; Session altered. SQL> select count(*) from TEST_DATA_ARCH_PART; COUNT(*) ---------- 1000000 ----// ----// No records present in the ACTIVE partition //---- ----// SQL> select count(*) from TEST_DATA_ARCH_PART partition (p_active); COUNT(*) ---------- 0 ----// ----// records are now moved to ARCHIVED partition //---- ----// SQL> select count(*) from TEST_DATA_ARCH_PART partition (p_archived); COUNT(*) ---------- 1000000 Let's check how much space is consumed by the ARCHIVED records by querying the database segments as shown below. SQL> select owner,segment_name as "Table Name",tablespace_name,sum(bytes)/1024/1024 Size_MB 2 from dba_segments where segment_name='TEST_DATA_ARCH_PART' group by owner,segment_name,tablespace_name; OWNER Table Name TABLESPACE_NAME SIZE_MB ------------- ------------------------- -------------------- ---------- MYAPP TEST_DATA_ARCH_PART ARCHDATA 16 MYAPP TEST_DATA_ARCH_PART APPDATA 40 We could see the archived records are moved in a compressed format to the Tier-2 tablespace ARCHDATA. However, the space from Tier-1 tablespace APPDATA is not yet released. We need to manually reclaim this space as show below. ----// ----// reclaiming unused space from the table //---- ----// SQL> alter table TEST_DATA_ARCH_PART shrink space; Table altered. SQL> select owner,segment_name as "Table Name",tablespace_name,sum(bytes)/1024/1024 Size_MB 2 from dba_segments where segment_name='TEST_DATA_ARCH_PART' group by owner,segment_name,tablespace_name; OWNER Table Name TABLESPACE_NAME SIZE_MB ------------- ------------------------- -------------------- ---------- MYAPP TEST_DATA_ARCH_PART ARCHDATA 12.9375 MYAPP TEST_DATA_ARCH_PART APPDATA .1875 As expected, all the records are ARCHIVED and are stored in a COMPRESSED format (Size: ~ 13 MB) on a low level storage device (ARCHDATA) and the unused space is reclaimed from the Tier-1 storage APPDATA. This type of setup and utilization of "In Database Archival" (Row Archival) would help us optimize the space required to store archived data within the same table (database). Note: We may consider using DBMS_REDEFINITION as an alternate to SHRINK command for reorganizing and reclaiming space ONLINE . Query Optimization for In Database Archiving In Database Archiving may lead to potential plan change or performance degradation when data is queried from the tables. This is due to the fact that, the query is transformed to add a filter condition to exclude the ARCHIVED records from query result. Let's quickly go through a simple demonstration to illustrate these facts. Demonstration I am using the same table from the previous example. As part of the last demonstration, we had archived all the table records. Let's populate the table with some ACTIVE records. ----// ----// populating table with ACTIVE records //---- ----// SQL> insert /*+ APPEND */ into TEST_DATA_ARCH_PART 2 select rownum+1e6, rpad('X',15,'X'), sysdate 3 from dual connect by rownum commit; Commit complete. We have populated the table with 1000000 ACTIVE records. Let's validate the records from the table. ----// ----// ACTIVE records from the table //---- ----// SQL> select count(*) from TEST_DATA_ARCH_PART; COUNT(*) ---------- 1000000 ----// ----// enabling row archival visibility //---- ----// SQL> alter session set ROW ARCHIVAL VISIBILITY=ALL; Session altered. ----// ----// Total records from the table //---- ----// SQL> select count(*) from TEST_DATA_ARCH_PART; COUNT(*) ---------- 2000000 SQL> select count(*) from TEST_DATA_ARCH_PART partition (p_active); COUNT(*) ---------- 1000000 SQL> select count(*) from TEST_DATA_ARCH_PART partition (p_archived); COUNT(*) ---------- 1000000 At this point, we have 2000000 records in the table, out of which 1000000 are ACTIVE and 1000000 are in ARCHIVED state. Let's query few records from the table and see how the SQL optimizer handles it. In the following example, I am querying records with ID ranging between 999000 and 1000005. The query should return only 4 records as the first 1000000 records are in ARCHIVED state. ----// ----// disabling row archival visibility //---- ----// SQL> alter session set ROW ARCHIVAL VISIBILITY=ACTIVE; Session altered. ----// ----// selecting records from table //---- ----// SQL> select /*+ gather_plan_statistics */ * from TEST_DATA_ARCH_PART 2 where id > 999000 and id 999000 AND "TEST_DATA_ARCH_PART"."ID" 999000 Now, lets take a look at the execution plan of this query. ----// ----// Query plan from the optimizer //---- ----// SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 3vqgzvvmj3wb9, child number 0 ------------------------------------- select /*+ gather_plan_statistics */ * from TEST_DATA_ARCH_PART where id > 999000 and id 999000 AND "ID" create index TEST_DATA_ARCH_PART_PK on TEST_DATA_ARCH_PART (ID, ORA_ARCHIVE_STATE); Index created. ----// ----// Disabling and dropping the exiting primary key //---- ----// SQL> alter table TEST_DATA_ARCH_PART disable constraint PK_TEST_DATA_ARCH_PART; Table altered. SQL> alter table TEST_DATA_ARCH_PART drop constraint PK_TEST_DATA_ARCH_PART; Table altered. ----// ----// Creating primary key using the new Index //---- ----/ / SQL> alter table TEST_DATA_ARCH_PART add constraint PK_TEST_DATA_ARCH_PART primary key (ID) using index TEST_DATA_ARCH_PART_PK; Table altered. We have modified the primary key index to include ORA_ARCHIVE_STATE in the index definition. Let's check, how the optimizer now handles the SQL query. ----// ----// Query records from table //---- ----// SQL> select /*+ gather_plan_statistics */ * from TEST_DATA_ARCH_PART 2 where id > 999000 and id select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); PLAN_TABLE_OUTPUT ----------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 2zgf279wu9291, child number 0 ------------------------------------- select /*+ gather_plan_statistics */ * from TEST_DATA_ARCH_PART where id > 999000 and id 999000 AND "TEST_DATA_ARCH_PART"."ORA_ARCHIVE_STATE"='0' AND "ID"<1000005) filter("TEST_DATA_ARCH_PART"."ORA_ARCHIVE_STATE"='0') Note ----- - dynamic statistics used: dynamic sampling (level=2) 25 rows selected. As we can see, SQL optimizer is now filtering at the access level. Now, it is fetching only 4 records rather than 1004 records when compared to the earlier execution plan. The modified index has helped the optimizer to eliminate unnecessary I/O while fetching the records. Conclusion When configuring In Database Archival , we should consider partitioning the table on the ORA_ARCHIVE_STATE column to optimize the space utilization for ARCHIVED records. Don't forget to enable ROW MOVEMENT in the table for archiving to work. Optionally, we may also need to consider reclaiming unused space on a periodic basis which would be left over due to the data movement between ACTIVE and ARCHIVED partitions. We should also consider appending the ORA_ARCHIVE_STATE column in all of the table indexes to address any performance degradation resulted from In Database Archival, while querying records from the tables. Reference Potential SQL Performance Degradation When Using "In Database Row Archiving" (Doc ID 1579790.1)
↧