Tuesday, January 13, 2009

Basic Data Guard Architecture



Primary and standby databases (database roles)

In a standby environment, one database is a primary database while all others are standby databases. Standby databases are either physical or logical standby databases.

Query the database_role column in v$database in order to find out the role of a database.

A standby database environment is meant for disastrous failures. It protects the data even if a machine is wrecked beyond recognition. Therefore, it makes no sense to put a primary and a standby database on the same hardware.

A logical standby turns redo into inserts, updates and deletes, while a physical standby directly applies redo to its datafiles..

Primary database

In a standby environment, exactly one database is a primary database. All the other databases are standby databases.

Logical standby database

The logical standby database is new with Oracle 9iR2.
The key advantage for logical standby databases is that they’re opened read/write, even while they’re in applied mode. That is, they can be used to generate reports and the like. It is indeed a fully functional database. Also, additional indexes, materialized views and so on can be created.
However (this being a disadvantage) not all datatypes are supported.
Oracle’s log apply services use the primary database’s redo log, transforms them into SQL statements and replays them on the logical standby database.

Physical standby database

A physical standby database is a byte for byte exact copy of the primary database. This also means that rowids stay the same in a physical standby database environment.
The log apply service uses the primary database’s redo log to recover the physical database.
A physical standby database might be opened read only, however, the received logs are in this case not applied. When the logs are applied, the database is not accessible (it is then in a managed recovery state).


Database Synchronization Options

Data Guard can be configured to run with varying synchronization modes indicating the potential for data loss:

Maximum Protection

Maximum protection ensures the highest level of data availability for the primary database. In maximum protection mode, redo log records are synchronously sent by LGWR to the standby database. Primary database changes are not committed until it has been confirmed that the data is available on at least one standby database.

Maximum Availability

Maximum availability provides the second highest level of data availability. As with its maximum reliability counterpart, redo data is synchronously transmitted from the primary database to the standby database by LGWR. The standby database may temporarily lag behind, or diverge, from the primary database without negatively impacting the production environment. If the standby database becomes unavailable for any reason, the protection mode is temporarily lowered to maximum performance until the problem has been corrected.

Maximum Performance

Maximum performance is the default protection mode. It offers lower data availability and higher performance than its counterparts.
Redo log data is asynchronously shipped to the standby database by either LGWR or ARCH. The commit operation on the primary database is not contingent upon the data being received by the standby server.
If all of the standby servers become unavailable, processing will continue on the primary database.

Data Guard Broker

Oracle’s Data Guard Broker is the management framework that is used to administer a Data Guard environment. An Oracle background server process called DMON is started on every site that is managed by the broker.

Reading Statspack


In Oracle, Performance Tuning is based on the following formula:

Response Time = Service Time + Wait Time

Where

  • Service Time is time spent on the CPU
  • Wait Time is the sum of time spent on Wait Events i.e. non-idle time spent waiting for an event to complete or for a resource to become available.

Service Time is comprised of time spent on the CPU for Parsing, Recursive CPU usage (for PLSQL and recursive SQL) and CPU used for execution of SQL statements (CPU Other).

Service Time = CPU Parse + CPU Recursive + CPU Other

The above components of Service Time can be found from the following statistics:

· Service Time from CPU used by this session

· CPU Parse from parse time cpu

· CPU Recursive from recursive cpu usage

From these, CPU Other can be calculated as follows:

CPU other = CPU used by this session - parse time CPU - recursive CPU usage

Many performance-tuning tools (including Statspack) produce a list of the top wait events. For example, Statspack’s report contains the "Top 5 Wait Events" section.(Pre-Oracle9i Release 2).

It is a common mistake to start dealing with Wait Events first and not taking in consideration the corresponding response time. So always compare the time consumed by the top wait events to the 'CPU used by this session' and identify the biggest consumers.

Here is an example where CPU Other was found to be a significant component of total Response Time even though the report shows direct path read as top wait event:

Top 5 Wait Events

Events

Waits

Wait Time(cs)

% Total Wt Time

direct path read

4232

10827

52.01

db file scattered read

6105

6264

30.09

direct path write

1992

3268

15.70

control file parallel write

893

198

.95

db file parallel write

40

131

.63

Statistic

Total

Per Second

Per Trans

CPU used by this session

358806

130.5

12372.6

parse time cpu

38

0.0

1.3

recursive cpu usage

186636

67.9

6435.7

From these figures we can obtain:

· Wait Time = 10,827 x 100% / 52,01% = 20,817 cs

· Service Time = 358,806 cs

· Response Time = 358,806 + 20,817 = 379,623 cs

· CPU Other = 358,806 - 38 - 186,636 = 172,132 cs

If we now calculate percentages for the top Response Time components:

· CPU Other = 45.34%

· CPU Recursive = 49.16%

· direct path read = 2.85%

· etc. etc.

So we can see the I/O-related Wait Events actually are not a significant component of the overall Response Time. For us it makes sense concentrate our tuning effort on the service time component.

CPU Other is a significant component of Response Time, so a possible next step is to look at the CPU intensive SQL and not at direct path read wait event.

Starting with Oracle9i Release 2, Statspack presents Service Time (obtained from the statistic CPU used by this session) together with the top Wait Events in a section called Top 5 Timed Events, which replaces the section Top 5 Wait Events of previous releases.

Here is an example:

Top 5 Timed Events

Events

Waits

Time(s)

% Total Ela Time

library cache lock

141

424

76.52

db file scattered read

3367

96

17.4

CPU time

32

5.79

db file sequential read

161

1

.18

control file parallel write

40

0

.05

Statistic

Total

Per Second

Per Trans

CPU used by this session

3211

4.3

1605.5

parse time cpu

59

0.1

29.5

recursive cpu usage

232

0.3

116.0

These figures give us directly the percentages of the Wait Events against the total Response Time so no further calculations are necessary to assess the impact of Wait Events. Service Time is presented as CPU time in this section and corresponds to the total CPU utilisation. We can drill down to the various components of Service Time as follows:

· CPU Other = 3,211 - 59 - 232 = 2,920 cs

· CPU Other = 2,920 / 3,211 x 5.79% = 5.26%

· CPU Parse = 59 / 3,211 x 5.79% = 0.11%

· CPU Recursive = 232 / 3,211 x 5.79% = 0.42%

In this example, the main performance problem was an issue related to the Library Cache.
The second most important time consumer was waiting for physical I/O due to multiblock reads (db file scattered read).

Identifying problematic SQL’s from Statspack

From the above calculations you will get the significant components which caused the performance problem. Based on this components lets decide on the various Statspack section to identify the problematic SQL’s.

  • Other CPU

If this shows CPU other as being significant the next step will be to look at the SQL performing most block accesses in the SQL by Gets section of the Statspack report. A better execution plan for this statement resulting in fewer Gets/Exec will reduce its CPU consumption.

  • CPU Parse

If CPU Parse time is a significant component of Response Time, it can be because cursors are repeatedly opened and closed every time they are executed instead of being opened once, kept open for multiple executions and only closed when they are no longer required. The SQL ordered by Parse Calls can help find such cursors.

  • Disk I/O related waits.

Identifying SQL statements responsible for most physical reads from the Statspack section SQL ordered by Reads has similar concepts as for SQL ordered by Gets.
% Total can be used to evaluate the impact of each statement. Reads per Exec together with Executions can be used as a hint of whether the statement has a suboptimal execution plan causing many physical reads or if it is there simply because it is executed often. Possible reasons for high Reads per Exec are use of unselective indexes require large numbers of blocks to be fetched where such blocks are not cached well in the buffer cache, index fragmentation, large Clustering Factor in index etc
.

  • Latch related waits.

Statspack has 2 sections to help find such unsharable statements, SQL ordered by Sharable Memory and SQL ordered by Version Count. This can help with Shared Pool and Library Cache/Shared Pool latch tuning. Statements with many versions (multiple child cursors with the same parent cursor i.e. identical SQL text but different properties such as owning schema of objects, optimizer session settings, types & lengths of bind variables etc.) are unsharable. This means they can consume excessive memory resources in the Shared Pool and cause performance problems related to parsing e.g. Library Cache and Shared Pool latch contention or lookup time e.g. Library Cache latch contention.

script for cold backup

set head off
set feedback off
set term on
prompt COLD BACKUP in progress .....

set term off

spool c:\dbcopy.bat

select 'copy '||name||' c:\COLDBACKUP' from v$datafile;
select 'copy '||name||' c:\COLDBACKUP' from v$controlfile;
select 'copy '||name||' c:\COLDBACKUP' from v$tempfile;
select 'copy '||member||' c:\COLDBACKUP' from v$logfile;

spool off

shutdown IMMEDIATE
host md c:\COLDBACKUP
host c:\dbcopy.bat
startup
set term on

host del c:\dbcopy.bat
prompt COLD BACKUP successfully completed.

set head on
set feedback on

SCRIPT FOR HOT BACKUP

set head off
set feedback off
set term on
prompt HOT BACKUP in progress .....

set term off

alter database begin backup;

spool c:\dbcopy.bat
select 'copy '||name||' C:\HOTBACKUP' from v$datafile;
select 'copy '||name||' c:\HOTBACKUP' from v$tempfile;


spool off


host md c:\HOTBACKUP
host c:\dbcopy.bat

set term on

host del c:\dbcopy.bat

alter database backup controlfile to 'c:\HOTBACKUP\control.ctl';
alter database end backup;
alter system switch logfile;
prompt HOT BACKUP successfully completed.


set head on
set feedback on

Script to tune cache hit ratio

REM: Date Submitted: 19-May-2006
REM: Date Posted: 30-May-2006
REM:
REM:*****************************************
REM: NOTE: PLEASE TEST THIS SCRIPT BEFORE USE.
REM: Author will not be responsible for any damage that may be cause by this script.
REM:*****************************************


DECLARE
ROWCACHE NUMBER(10,2);
BEGIN
Select ((sum(getmisses) / sum(gets))*100) INTO ROWCACHE From v$rowcache;
IF ROWCACHE<15 then
dbms_output.put_line(\'TUNING THE CACHE HIT RATIO IS less than 15% This value is ok \'||rowcache ||\'%\');
end if;
IF ROWCACHE>15 then
dbms_output.put_line(\'increase the initialisation parameter SHARED_POOL_SIZE \'||rowcache);
end if;
END;
/

Script to tune library cache

REM: Script to tune library cache
REM:
REM: Author: Aravind Kannan
REM: Date Submitted: 19-May-2006
REM: Date Posted: 30-May-2006
REM:
REM:*****************************************
REM: NOTE: PLEASE TEST THIS SCRIPT BEFORE USE.
REM: Author will not be responsible for any damage that may be cause by this script.
REM:*****************************************


DECLARE
hit NUMBER(10,2);
reload number(10,2);
begin
Select ((sum(pinhits) / sum(pins))*100) into hit From v$librarycache Where namespace in (\'SQL AREA\', \'TABLE/PROCEDURE\',\'BODY\', \'TRIGGER\');
select ((sum(reloads)/sum(pins))*100) into reload From v$librarycache Where namespace in (\'SQL AREA\', \'TABLE/PROCEDURE\', \'BODY\', \'TRIGGER\');
IF hit<85 then
dbms_output.put_line(\'The hit ratio should be at least 85% (i.e. 0.85). this is ok \'||hit);
end if;
IF hit>85 then
dbms_output.put_line(\'THE hit ratio >85%. check shared pool and open cursor parameter \'||hit);
end if;
dbms_output.put_line(\'*************************************************************************\');
if reload>.02 then
dbms_output.put_line(\'The reload percent should be very low, 2% (i.e. 0.02) or less. this is ok \'||reload);
end if;
if reload<.02 then
dbms_output.put_line(\'The reload >2% \'||reload);
end if;
end;
/

What is Table fragmentation?

When rows are not stored contiguously, or if rows are split onto more than one page, performance decreases because these rows require additional page accesses. Table fragmentation is distinct from file fragmentation.

When lots of DML operation apply on tables then tables is fragmented.

because DML is not release free space from table below HWM.

Hint: HWM is indicator for USED BLOCKS in database. Blocks below the high water mark (used blocks) have at least once contained data. This data might have been deleted.

Since Oracle knows that blocks beyond the high water mark don't have data, it only reads blocks up to the high water mark in a full table scan.

DDL statement always reset HWM.

How to find table fragmentation?

SQL> select count(*) from big1;

1000000 rows selected.

SQL> delete from big1 where rownum <= 300000;

300000 rows deleted.

SQL> commit;

Commit complete.

SQL> update big1 set object_id = 0 where rownum <=350000;

342226 rows updated.

SQL> commit;

Commit complete.

SQL> exec dbms_stats.gather_table_stats('SCOTT','BIG1');

PL/SQL procedure successfully completed.

Table Size ( with fragmented)

SQL> select table_name,round((blocks*8),2)||'kb' "size"
2 from user_tables
3 where table_name = 'BIG1';

TABLE_NAME size
------------------------------ ------------------------------------------

BIG1 72952kb

Actual data in table

SQL> select table_name,round((num_rows*avg_row_len/1024),2)||'kb' "size"
2 from user_tables
3 where table_name = 'BIG1';

TABLE_NAME size
------------------------------ ------------------------------------------
BIG1 30604.2kb

Note= 72952 – 30604 = 42348 Kb is wasted space in table

The difference between two values is 60% and Pctfree 10% (default) so table is 50% extra space which is wasted because there is no data.

How to reset HWM / remove fragemenation?

For that we need to reorganize fragmented table

We have four options to reorganize fragmented tables

1. alter table … move + rebuild indexes

2. export / truncate / import

3. create table as select ( CTAS)

4. dbms_redefinition

Option: 1 “alter table … move + rebuild indexes”

SQL> alter table BIG1 move;

Table altered.

SQL> select status,index_name from user_indexes

2 where table_name = 'BIG1';

STATUS INDEX_NAME

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

UNUSABLE BIGIDX

SQL> alter index bigidx rebuild;

Index altered.

SQL> select status,index_name from user_indexes

2 where table_name = 'BIG1';

STATUS INDEX_NAME

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

VALID BIGIDX

SQL> exec dbms_stats.gather_table_stats('SCOTT','BIG1');

PL/SQL procedure successfully completed.

SQL> select table_name,round((blocks*8),2)||'kb' "size"

2 from user_tables

3 where table_name = 'BIG1';

TABLE_NAME size

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

BIG1 38224kb

SQL> select table_name,round((num_rows*avg_row_len/1024),2)||'kb' "size"

2 from user_tables

3 where table_name = 'BIG1';

TABLE_NAME size

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

BIG1 30727.37kb

Option: 2 “Create table as select”

SQL> create table big2 as select * from big1;

Table created.

SQL> drop table big1 purge;

Table dropped.

SQL> rename big2 to big1;

Table renamed.

SQL> exec dbms_stats.gather_table_stats('SCOTT','BIG1');

PL/SQL procedure successfully completed.

SQL> select table_name,round((blocks*8),2)||'kb' "size"

2 from user_tables

3 where table_name = 'BIG1';

TABLE_NAME size

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

BIG1 85536kb

SQL> select table_name,round((num_rows*avg_row_len/1024),2)||'kb' "size"

2 from user_tables

3 where table_name = 'BIG1';

TABLE_NAME size

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

BIG1 68986.97kb

SQL> select status from user_indexes

2 where table_name = 'BIG1';

no rows selected

SQL> -- Note we need to create all indexes.

Option: 3 “export / truncate / import”

SQL> select table_name, round((blocks*8),2)||'kb' "size"

2 from user_tables

3 where table_name = 'BIG1';

TABLE_NAME size

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

BIG1 85536kb

SQL> select table_name, round((num_rows*avg_row_len/1024),2)||'kb' "size"

2 from user_tables

3 where table_name = 'BIG1';

TABLE_NAME size

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

BIG1 42535.54kb

SQL> select status from user_indexes where table_name = 'BIG1';

STATUS

--------

VALID

SQL> exit

Disconnected from Oracle Database 10g Enterprise Edition Release 10.1.0.5.0 - Pr

oduction

With the Partitioning, OLAP and Data Mining options

C:>exp scott/tiger@Orcl file=c:big1.dmp tables=big1

Export: Release 10.1.0.5.0 - Production on Sat Jul 28 16:30:44 2007

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Connected to: Oracle Database 10g Enterprise Edition Release 10.1.0.5.0 - Produc

tion

With the Partitioning, OLAP and Data Mining options

Export done in WE8MSWIN1252 character set and AL16UTF16 NCHAR character set

About to export specified tables via Conventional Path ...

. . exporting table BIG1 468904 rows exported

Export terminated successfully without warnings.

C:>sqlplus scott/tiger@orcl

SQL*Plus: Release 10.1.0.5.0 - Production on Sat Jul 28 16:31:12 2007

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Connected to:

Oracle Database 10g Enterprise Edition Release 10.1.0.5.0 - Production

With the Partitioning, OLAP and Data Mining options

SQL> truncate table big1;

Table truncated.

SQL> exit

Disconnected from Oracle Database 10g Enterprise Edition Release 10.1.0.5.0 - Pr

oduction

With the Partitioning, OLAP and Data Mining options

C:>imp scott/tiger@Orcl file=c:big1.dmp ignore=y

Import: Release 10.1.0.5.0 - Production on Sat Jul 28 16:31:54 2007

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Connected to: Oracle Database 10g Enterprise Edition Release 10.1.0.5.0 - Produc

tion

With the Partitioning, OLAP and Data Mining options

Export file created by EXPORT:V10.01.00 via conventional path

import done in WE8MSWIN1252 character set and AL16UTF16 NCHAR character set

. importing SCOTT's objects into SCOTT

. . importing table "BIG1" 468904 rows imported

Import terminated successfully without warnings.

C:>sqlplus scott/tiger@orcl

SQL*Plus: Release 10.1.0.5.0 - Production on Sat Jul 28 16:32:21 2007

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Connected to:

Oracle Database 10g Enterprise Edition Release 10.1.0.5.0 - Production

With the Partitioning, OLAP and Data Mining options

SQL> select table_name, round((blocks*8),2)||'kb' "size"

2 from user_tables

3 where table_name = 'BIG1';

TABLE_NAME size

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

BIG1 85536kb

SQL> select table_name, round((num_rows*avg_row_len/1024),2)||'kb' "size"

2 from user_tables

3 where table_name = 'BIG1';

TABLE_NAME size

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

BIG1 42535.54kb

SQL> exec dbms_stats.gather_table_stats('SCOTT','BIG1');

PL/SQL procedure successfully completed.

SQL> select table_name, round((blocks*8),2)||'kb' "size"

2 from user_tables

3 where table_name = 'BIG1';

TABLE_NAME size

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

BIG1 51840kb

SQL> select table_name, round((num_rows*avg_row_len/1024),2)||'kb' "size"

2 from user_tables

3 where table_name = 'BIG1';

TABLE_NAME size

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

BIG1 42542.27kb

SQL> select status from user_indexes where table_name = 'BIG1';

STATUS

--------

VALID

SQL> exec dbms_redefinition.can_redef_table('SCOTT','BIG1',-

> dbms_redefinition.cons_use_pk);

PL/SQL procedure successfully completed.

Option: 4 “dbms_redefinition”

SQL> create table TABLE1 (

2 no number,

3 name varchar2(20) default 'NONE',

4 ddate date default SYSDATE);

Table created.

SQL> alter table table1 add constraint pk_no primary key(no);

Table altered.

SQL> begin

2 for x in 1..100000 loop

3 insert into table1 ( no , name, ddate)

4 values ( x , default, default);

5 end loop;

6 end;

7 /

PL/SQL procedure successfully completed.

SQL> create or replace trigger tri_table1

2 after insert on table1

3 begin

4 null;

5 end;

6 /

Trigger created.

SQL> select count(*) from table1;

COUNT(*)

----------

100000

SQL> delete table1 where rownum <= 50000;

50000 rows deleted.

SQL> commit;

Commit complete.

SQL> exec dbms_stats.gather_table_stats('SCOTT','TABLE1');

PL/SQL procedure successfully completed.

SQL> select table_name, round((blocks*8),2)||'kb' "size"

2 from user_tables

3 where table_name = 'TABLE1';

TABLE_NAME size

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

TABLE1 2960kb

SQL> select table_name, round((num_rows*avg_row_len/1024),2)||'kb' "size"

2 from user_tables

3 where table_name = 'TABLE1';

TABLE_NAME size

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

TABLE1 822.69kb

SQL> --Minimum Privs required "DBA" role or "SELECT" on dbms_redefinition pkg

SQL> --First check table is condidate for redefinition.

SQL>

SQL> exec sys.dbms_redefinition.can_redef_table('SCOTT',-

> 'TABLE1',-

> sys.dbms_redefinition.cons_use_pk);

PL/SQL procedure successfully completed.

SQL> --After verifying that the table can be redefined online, you manually crea

te an empty interim table (in the same schema as the table to be redefined)

SQL>

SQL> create table TABLE2 as select * from table1 WHERE 1 = 2;

Table created.

SQL> exec sys.dbms_redefinition.start_redef_table ( 'SCOTT',-

> 'TABLE1',-

> 'TABLE2');

PL/SQL procedure successfully completed.

SQL> --This procedure keeps the interim table synchronized with the original tab

le.

SQL>

SQL> exec sys.dbms_redefinition.sync_interim_table ('SCOTT',-

> 'TABLE1',-

> 'TABLE2');

PL/SQL procedure successfully completed.

SQL> --Create PRIMARY KEY on interim table(TABLE2)

SQL> alter table TABLE2

2 add constraint pk_no1 primary key (no);

Table altered.

SQL> create trigger tri_table2

2 after insert on table2

3 begin

4 null;

5 end;

6 /

Trigger created.

SQL> --Disable foreign key on original table if exists before finish this proces

s.

SQL>

SQL> exec sys.dbms_redefinition.finish_redef_table ( 'SCOTT',-

> 'TABLE1',-

> 'TABLE2');

PL/SQL procedure successfully completed.

SQL> exec dbms_stats.gather_table_stats('SCOTT','TABLE1');

PL/SQL procedure successfully completed.

SQL> select table_name, round((blocks*8),2)||'kb' "size"

2 from user_tables

3 where table_name = 'TABLE1';

TABLE_NAME size

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

TABLE1 1376kb

SQL> select table_name, round((num_rows*avg_row_len/1024),2)||'kb' "size"

2 from user_tables

3 where table_name = 'TABLE1';

TABLE_NAME size

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

TABLE1 841.4kb

SQL> select status,constraint_name

2 from user_constraints

3 where table_name = 'TABLE1';

STATUS CONSTRAINT_NAME

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

ENABLED PK_NO1

SQL> select status ,trigger_name

2 from user_triggers

3 where table_name = 'TABLE1';

STATUS TRIGGER_NAME

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

ENABLED TRI_TABLE2

SQL> drop table TABLE2 PURGE;

Table dropped.