Archiv für die Kategorie TOI

Logical Standby in NOARCHIVELOG mode

This week, I was teaching an 11g Data Guard course in Duesseldorf – my home location. One question that arose in that course was, whether it is possible to run a Logical Standby Database not beeing in archive mode.

Brief explanation: Unlike Physical Standby Databases, the Logical Standby is a quite independent Database that tries to do the same SQL Statements that arose at the Primary to keep up to date with it. In order to be able to do so, it gets the redo protocol transmitted from the primary and analyzes it (with Logminer technique). That redo protocol is stored in Archive Logs on the Logical Standby, which can’t be turned off, of course.

But also, when the SQL Apply on the standby changes blocks, it writes its own redo protocol into its own Online Logfiles and (by default) archives them. Sometimes, this is not needed, because customers use their Logical Standby as kind of „cheap replication“ and do not use it for HA purpose. So can we turn off Archive Logging on the Logical Standby? I saw that in place already at a (not reference, unfortunately) customer site, so the short answer was:  Yes, we can 🙂

I tested it then myself and it really works. We can turn off archivelogging on the Logical Standby even with usage of the Data Guard Broker and with no warnings from it. But it seems, as if that is only possible with archiver transmission from the Primary to the Logical Standby. Otherwise (with LogXptMode async or sync), the transmission to the Logical Standby stops.

7 Kommentare

Retrieve old versions of procedures with Flashback Query

rewind

Each stored procedure (or trigger, package etc.) is reflected in DBA_SOURCE, of course. Now imagine, you modified or even dropped a procedure without having the code available to recreate it properly. In this case, you may save the day by using Flashback Query (possible since 9i) against DBA_SOURCE, like in this example:

SQL> select text from dba_source where name='UPD_SALES' order by line;

TEXT
--------------------------------------------------------------------------------
procedure upd_sales as
begin
for i in 1..10000 loop
update sales set amount_sold=amount_sold*1 where rownum<2;
commit;
end loop;
end;

7 rows selected.

SQL> drop procedure upd_sales;

Procedure dropped.

SQL> connect / as sysdba
Connected.

SQL> select text from dba_source 
     as of timestamp systimestamp - interval '5' minute  
     where name='UPD_SALES' order by line;

TEXT
--------------------------------------------------------------------------------
procedure upd_sales as
begin
for i in 1..10000 loop
update sales set amount_sold=amount_sold*1 where rownum<2;
commit;
end loop;
end;

7 rows selected.

I could spool the output from above into a text file and use it afterwards to recreate my procedure as it was before 5 minutes! By the way, I know that the commit inside the loop is not a good idea here 🙂

17 Kommentare

Reducing Buffer Busy Waits with Automatic Segment Space Management & Hash Partitioning

Last week, I was in Frankfurt (Germany) teaching about Performance Tuning & Partitioning in a Private Event for LHS Telecommunication. One demonstration that I developed for that event was about Reducing Buffer Busy Waits. One major reason why the wait event Buffer Busy Waits can occur is if many sessions are inserting at the same time in the same table. Large OLTP installations therefore will see it most likely. This can lead to a performance problem, especially if the site does not make use of Automatic Segment Space Management – a technique introduced in 9i and used by default since 10g. The old fashioned way to determine where to insert a new row in blocks before the high water mark uses Freelists. So if you are going with that „traditional“ technique with freelists (and PCTUSED), then your segment has one freelist by default, which leads to contention easy, because multiple simultaneous sessions that insert look at that one freelist and all pick the same Blocks to insert in. Here is an example:

SQL> create tablespace old datafile '/u01/app/oracle/oradata/orcl/old01.dbf'
     size 50m segment space management manual;

Tablespace created.

SQL> create table bbw_freelist (n number, name varchar2(50)) tablespace old;

Table created.

That table now uses (one) freelist and PCTUSED (40 by default) in order to determine what blocks may be used

for insert. The next steps are going to create 10 simultaneous sessions that insert 1 Million rows into the table.

SQL> create or replace procedure bufwait1 as
begin
for i in 1..100000 loop
insert into bbw_freelist values (i, 'BUFFER BUSY WAITS?');
end loop;
commit;
end;
/  2    3    4    5    6    7    8

Procedure created.

SQL> create or replace procedure do_bbw1 as
v_jobno number:=0;begin
FOR i in 1..10 LOOP
 dbms_job.submit(v_jobno,'bufwait1;', sysdate);
END LOOP;
commit;
end;
/  2    3    4    5    6    7    8

Procedure created.
SQL> exec do_bbw1

PL/SQL procedure successfully completed.

SQL> select count(*) from dba_jobs_running

COUNT(*)
----------
0
One easy way to discover Buffer Busy Waits is v$segment_statistics.

Of course, you may see them also in Statspack/AWR reports.

SQL> select sum(value) from v$segment_statistics
 where OBJECT_NAME='BBW_FREELIST' and statistic_name='buffer busy waits';

SUM(VALUE)
----------
 1623

Next we are doing exactly the same inserts with a table that uses Automatic Segment Space Management.

Simply speaking, ASSM achieves a better distribution of the new rows across multiple blocks if multiple

inserts occur at the same time. It is determined at the tablespace level:

SQL> select SEGMENT_SPACE_MANAGEMENT from dba_tablespaces where tablespace_name='USERS';

SEGMEN
------
AUTO

SQL> create table bbw_assm (n number, name varchar2(50)) tablespace users;

Table created.

create or replace procedure bufwait2 as
begin
for i in 1..100000 loop
insert into bbw_assm values (i, 'BUFFER BUSY WAITS?');
end loop;
commit;
end;
/

create or replace procedure do_bbw2 as
v_jobno number:=0;begin
FOR i in 1..10 LOOP   
 dbms_job.submit(v_jobno,'bufwait2;', sysdate);
END LOOP;
commit;
end;
/

SQL> exec do_bbw2

PL/SQL procedure successfully completed.

SQL> select count(*) from dba_jobs_running;

 COUNT(*)
----------
 10

SQL> select count(*) from dba_jobs_running;

 COUNT(*)
----------
 0

SQL> select sum(value) from v$segment_statistics
 where OBJECT_NAME='BBW_ASSM' and statistic_name='buffer busy waits';

SUM(VALUE)
----------
 295

As you can see, ASSM reduced strongly the number of Buffer Busy Waits! But still there are some,

and still this may be a performance problem for very large OLTP sites. Right now, the table is

not partitioned, so we have only one „hot extent“ if the table is continually growing, all inserts

occur in the newest allocated extent – where they are distributed nicely via ASSM. If that same table

would be Hash Partitioned, then we could have multiple „hot extents“ further more reducing the contention:

SQL> create table bbw_hash (n number, name varchar2(50))tablespace users
 partition by hash (n) partitions 256;

Table created.

SQL> create or replace procedure bufwait3 as
begin
for i in 1..100000 loop
insert into bbw_hash values (i, 'BUFFER BUSY WAITS?');
end loop;
commit;
end;
/  2    3    4    5    6    7    8

Procedure created.

SQL> create or replace procedure do_bbw3 as
v_jobno number:=0;begin
FOR i in 1..10 LOOP
 dbms_job.submit(v_jobno,'bufwait3;', sysdate);
END LOOP;
commit;
end;
/  2    3    4    5    6    7    8

Procedure created.

SQL> exec do_bbw3

PL/SQL procedure successfully completed.

SQL> select sum(value) from v$segment_statistics
 where OBJECT_NAME='BBW_HASH' and statistic_name='buffer busy waits';

SUM(VALUE)
----------
 226

We observe a (in this case) moderate further decrease of the Buffer Busy Waits. That

is one major reason for hash partitioning: Fighting against contention. Another reason

is – as in other kinds of partitioning – the possibilty for Partiton Pruning, if the

partition key is part of the where-clause of our statement. Then the optimizer

implicitly knows what partitions to scan. Even in my (relatively tiny) two tables, that

effect is visible:

SQL> set timing on
SQL> select * from bbw_assm where n=4711;

 N NAME
---------- --------------------------------------------------
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?

10 rows selected.

Elapsed: 00:00:00.11

SQL> select * from bbw_hash where n=4711;

 N NAME
---------- --------------------------------------------------
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?
 4711 BUFFER BUSY WAITS?

10 rows selected.

Elapsed: 00:00:00.01

,

7 Kommentare