Friday, 29 May 2020

ORA-39511: Start of CRS resource for instance '223' failed with error:[CRS-5702: Resource 'ora.asm' is already running on 'xvm009'

Problem abstract: 

While trying to startup a new instance manually in order to start duplicating one database I was getting below error:

SQL> startup nomount pfile=initxtd1.ora 
ORA-39511: Start of CRS resource for instance '223' failed with error:[CRS-5702: Resource 'ora.asm' is already running on 'xvm009'
CRS-0223: Resource 'ora.asm' has placement error.
clsr_start_resource:260 status:223
clsrapi_start_asm:start_asmdbs status:223


This is on a 19c database setup on a cluster environment with ASM.

Before I issue this command I've manually dropped a database having same name xtd, I didn't use dbca for this job because I had a problem login to the server with VNC, so I took the easy approach and dropped it manually and deleted all its files under ASM groups.

Fix:

It's always recommended dropping the database using DBCA, specially on a cluster/Oracle Restart environment. This will maintain to clean up all database resources on the server and avoid having above error.

For my scenario, because my job was not complete, I had to remove the remaining database cluster resources manually using the following commands:

1- Remove the database and its instances resources from OCR:
# srvctl remove instance -d xtd -i xtd1
# srvctl remove instance -d xtd -i xtd2
# srvctl remove database -d xtd


2- Remove all the database files inside ASM including datafiles, redo log files, controlfiles, password file, spfile from asmcmd console.
3- Remove the database entry from /etc/oratab

4- Make sure there are no resources belong to the dropped database:

# crsctl stat res -p


ORA-00600: internal error code, arguments: [krfg_mgen_coord2], [82], [2], [2], [], [], [], [], [], [], [], []

Problem: 

On one of 19c active data guard RAC database the recovery process was keep failing with below error:

ORA-00600: internal error code, arguments: [krfg_mgen_coord2], [82], [2], [2], [], [], [], [], [], [], [], []
ORA-10877: error signaled in parallel recovery slave
ORA-10877: error signaled in parallel recovery slave
ORA-10877: error signaled in parallel recovery slave
ORA-10877: error signaled in parallel recovery slave
Incident details in: /u01/oracle/diag/rdbms/sp/sp1/incident/incdir_1639057/sp1_mrp0_4991_i1639057.trc 


Fix:

I came to know it's Bug 12407536 | MRP PROCRESS CRASHES DUE TO ORA-600 [KRFG_MGEN_COORD2]
The workaround is to disable the FLASHBACK feature which was already enabled on that database:

SQL> ALTER DATABASE FLASHBACK OFF;
SQL> RECOVER MANAGED STANDBY DATABASE NODELAY DISCONNECT;

Sunday, 12 April 2020

"No space left on device" Error while there are plenty of free space

One week back I came across the following clusterware error:

2020-04-09 05:17:38.726 [OCSSD(27899)]CRS-10000: CLSU-00100: operating system function: mkdir failed with error data: 28
CLSU-00101: operating system error message: No space left on device
CLSU-00103: error location: authprep6
CLSU-00104: additional error information: failed to make dir /u01/12.1.0/grid/auth/css/fzpson06pe1p/A1673086


When checking /u01 filesystem, I found that it already has much of free space :


[oracle@fzpson06pe1p fzpson06pe1p]$ df -hP /u01
Filesystem                          Size  Used Avail Use% Mounted on
/dev/mapper/VolumeGroup00-LogVol07   99G   59G   36G  63% /u01


I remember I had a similar issue many years back and it was due inodes exhaustion.
When checked the inodes with df -i it was totally exhausted:

[oracle@fzpson06pe1p ~]$ df -i /u01
Filesystem                         Inodes IUsed IFree IUse% Mounted on
/dev/mapper/VolumeGroup00-LogVol07   6.3M  6.3M   0  100% /u01


And yes, running out of inodes can paralyze the filesystem as similar as running out of space.

But what are the inodes?
In short, inodes are the records that hold the metadata of the directories/files which stored under the filesystem. Metadata like; on which blocks the file is stored, its owner and permissions and so on...
Each Linux filesystem has a limited number of inodes depending on the filesystem size. This limitation of inodes limits the number of directories/files that can be stored under the filesystem as well.

Now, how to fix this situation?
Just delete/cleanup the unwanted files under the impacted filesystem. In my case the impacted filesystem was /u01 where Grid Infrastructure and Oracle DB software were installed. So, I cleaned up old audit logs and trace files to release the inodes.

Once I cleaned the audit files alone the inodes utilization dropped dramatically:

[oracle@fzpson06pe1p ~]$ df -i /u01
Filesystem                         Inodes IUsed IFree IUse% Mounted on
/dev/mapper/VolumeGroup00-LogVol07   6.3M  1.6M  4.7M   26% /u01


Note: In this situation, I'm not concerned about cleaning up big files as I'm concerned to delete as much unneeded files as possible.

In the next few days I'll update the dbalarm script with a new feature to monitor the inodes utilization in the filesystems as well. Stay tuned and download the latest version from here:
http://dba-tips.blogspot.com/2014/02/database-monitoring-script-for-ora-and.html

For more reading about inodes:
https://www.howtogeek.com/465350/everything-you-ever-wanted-to-know-about-inodes-on-linux/

Thursday, 1 August 2019

RMAN-06174: not connected to auxiliary database

Problem:
When duplicating from an active database I was getting this error:

...
sql statement: alter system set  db_name =  ''TESTDB2'' comment= ''Modified by RMAN duplicate'' scope=spfile

Oracle instance shut down


released channel: disk1

released channel: disk2
released channel: disk3
released channel: disk4
released channel: disk5
released channel: disk6
released channel: disk7
released channel: disk8
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of Duplicate Db command at 08/01/2019 14:24:24
RMAN-06174: not connected to auxiliary database
RMAN-03015: error occurred in stored script Memory Script
RMAN-04006: error from auxiliary database: ORA-12514: TNS:listener does not currently know of service requested in connect descriptor


Analysis:
The new instance was not properly registered with the listener on the destination server.
Registering the destination instance statically with the listener [on the destination server] has fixed the issue:

Solution:
In the destination/target server Add the lines in green color to the listener.ora file with the right destination/target instance name:
LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 172.5.5.21)(PORT = 1521))
    )
  )

SID_LIST_LISTENER =

  (SID_LIST =
    (SID_DESC =
      (SID_NAME = AWSUAT4DB2)
      (ORACLE_HOME = /u01/app/oracle/product/11.2.0/dbhome_1)
    )
  )

Then reload the listener: [On the Target/Destination Server]
# lsnrctl reload

Wednesday, 24 July 2019

ORA-38701 ORA-27037 when select * from v$restore_point;

When trying to find the name of a restore point that has one or all of its flashback logs not available you will get this error:

SQL> select * from v$restore_point;
select * from v$restore_point
              *
ERROR at line 1:
ORA-38701: Flashback database log 112 seq 10593 thread 1: "/fra/ERDB/flashback/o1_mf_gbd00tqk_.flb"
ORA-27037: unable to obtain file status
Linux-x86_64 Error: 2: No such file or directory
Additional information: 3

You have to get the restore point name from RMAN which read the details from the controlfile instead of the dictionary:

RMAN> list restore point all;
using target database control file instead of recovery catalog
SCN              RSP Time  Type       Time      Name
---------------- --------- ---------- --------- ----
6418212255444              GUARANTEED 01-JUL-19 TEMP

Drop the restore point from SQLPLUS:
SQL> drop restore point temp;

Monday, 22 July 2019

Unable to Drop a REDOLOG Group ORA-00313 ORA-27037 No such file or directory

Cannot drop a standby redo logfile group because its files were already deleted on OS:

SQL> alter database drop standby logfile group 26;
alter database drop standby logfile group 26
*
ERROR at line 1:
ORA-00313: open failed for members of log group 26 of thread 2
ORA-00312: online log 26 thread 2: '/data01/awsdev2/datafiles/group_26.273.968987155'
ORA-27037: unable to obtain file status
Linux-x86_64 Error: 2: No such file or directory
Additional information: 3
ORA-00312: online log 26 thread 2: '/data01/awsdev2/datafiles/group_26.273.968987153'
ORA-27037: unable to obtain file status
Linux-x86_64 Error: 2: No such file or directory
Additional information: 3

Fix:

Re-populate the logfile members inside this group:
SQL> alter database clear unarchived logfile group 26;

Database altered.

Now you can drop the standby redolog group:
SQL> alter database drop standby logfile group 26;

Database altered.

Monday, 15 July 2019

Pause/Resume/Kill a Running RMAN Backup

To Check if there is an RMAN backup is currently running:
col START_TIME for a15
col END_TIME for a15
col TIME_TAKEN_DISPLAY for a10
col INPUT_BYTES_DISPLAY heading "DATA SIZE" for a10
col OUTPUT_BYTES_DISPLAY heading "Backup Size" for a11
col OUTPUT_BYTES_PER_SEC_DISPLAY heading "Speed/s" for a10
col output_device_type heading "Device_TYPE" for a11
SELECT to_char (start_time,'DD-MON-YY HH24:MI') START_TIME,to_char(end_time,'DD-MON-YY HH24:MI') END_TIME, time_taken_display, status,input_type, output_device_type,input_bytes_display, output_bytes_display,output_bytes_per_sec_display,COMPRESSION_RATIO COMPRESS_RATIO FROM v$rman_backup_job_details WHERE status like 'RUNNING%';

To Pause an already running RMAN backup: [This is only applicable for Linux OS]
set pages 0 feedback off 
select 'TO PAUSE THE RUNNING RMAN BACKUP RUN THIS OS COMMAND:=>    kill -STOP '||listagg (p.spid, ' ')  WITHIN GROUP (ORDER BY p.spid) from v$session s, v$process p where s.program like 'rman@%' and p.addr=s.paddr;

To Resume an already "Paused" RMAN backup: [This is only applicable for Linux OS]
set pages 0 feedback off 
select 'TO RESUME A "PAUSED" RMAN BACKUP RUN THIS OS COMMAND:=>  kill -CONT '||listagg (p.spid, ' ')  WITHIN GROUP (ORDER BY p.spid) from v$session s, v$process p where s.program like 'rman@%' and p.addr=s.paddr;

To Terminate an already running RMAN backup: [This is only applicable for Linux OS]
set pages 0 feedback off 
select 'TO KILL  THE RUNNING RMAN BACKUP RUN THIS OS COMMAND:=>  kill -9 '||listagg (p.spid, ' ')  WITHIN GROUP (ORDER BY p.spid) from v$session s, v$process p where s.program like 'rman@%' and p.addr=s.paddr;

Thursday, 21 March 2019

Database Migration To A Different Filesystem On The Same Server With Minimal Downtime

The Objective is to move all Database files from old location [/csrprd/datafile/OEMDB] to the new location [/u02/oracle/oradata/OEMDB] within a minimal downtime window.

First Take an RMAN Backup As Copy to the new filesystem:
The below script will create a Copy Backup under the new filesystem /u02/oracle/oradata/OEMDB, if you re-run the same script for the second time it will refresh (sync) the already exist Backup Copy using a temporary incremental backup without the need to create a new Copy Backup from scratch which will save much time.

RMAN> run{
allocate channel F1 type disk format '/u02/oracle/oradata/OEMDB/%U';
allocate channel F2 type disk format '/u02/oracle/oradata/OEMDB/%U';
allocate channel F3 type disk format '/u02/oracle/oradata/OEMDB/%U';
BACKUP AS COMPRESSED BACKUPSET INCREMENTAL LEVEL 1 FOR RECOVER OF COPY WITH TAG 'DB_COPY_UPDTD_BKP'
DATABASE FORMAT '/u02/oracle/oradata/OEMDB/%d_%t_%s_%p'; # Incremental Level 1 Backup to recover the Image COPY.
RECOVER COPY OF DATABASE WITH TAG 'DB_COPY_UPDTD_BKP'; # Recover Image Copy with the Incr lvl1.
DELETE noprompt backup TAG 'DB_COPY_UPDTD_BKP'; # Delete [only] the incrmental bkp used for recovery.
#DELETE noprompt backup TAG 'arc_for_image_recovery' completed before 'sysdate-1'; # Delete Archive bkp for the previous recover.
DELETE noprompt copy   TAG 'ctrl_after_image_reco'; # Delete Controlfile bkp for the previous run.
BACKUP as copy current controlfile format '/u02/oracle/oradata/OEMDB/ctl_%U' tag 'ctrl_after_image_reco'; # Controlfile Copy Backup.
sql "ALTER DATABASE BACKUP CONTROLFILE TO TRACE AS ''/u02/oracle/oradata/OEMDB/controlfile.trc'' reuse"; # Controlfile Trace Backup.
sql "create pfile=''/u02/oracle/oradata/OEMDB/init${ORACLE_SID}.ora'' from spfile"; # Backup SPFILE.
}

In order to minimize the downtime during the migration, it's recommended to Sync the Copy Backup files using the same above command just before starting the actual migration.

Re-name the controlfile to point to the new location [/u02/oracle/oradata/OEMDB]:
Because I'm doing this practice on a test machine I'm keeping the control files on the same location which is not a best practice.

SQL> alter system set control_files='/u02/oracle/oradata/OEMDB/o1_mf_dmj045mz_.ctl','/u02/oracle/oradata/OEMDB/o1_mf_dmj045o9_.ctl' scope=spfile;

Generate the Shell command that will be used to move the controlfiles to the new filesystem during the actual migration:
SQL> sho parameter control
SQL> select 'cp '||name||'  /u02/oracle/oradata/OEMDB/'||SUBSTR(name, INSTR(name,'/', -1,1)+1) from v$controlfile;

Change all the parameters pointing to the old database files location to point to the new location:
Only if these parameters are already set:
SQL> ALTER SYSTEM SET db_create_file_dest='/u02/oracle/oradata/OEMDB'         SCOPE=SPFILE;
SQL> ALTER SYSTEM SET db_create_online_log_dest_1='/u02/oracle/oradata/OEMDB' SCOPE=SPFILE;
-- In case the Archivelogs and Flashback logs will be stored in the new location:
SQL> ALTER SYSTEM SET db_recovery_file_dest='/u02/oracle/oradata/OEMDB'       SCOPE=SPFILE;
SQL> ALTER SYSTEM SET log_archive_dest_1='LOCATION=/u02/oracle/oradata/OEMDB' SCOPE=SPFILE;

Actual Database Files Migration:
SQL> shu immediate
 -- Copy the controlfiles to new location:
 # cp  /csrprd/datafile/OEMDB/o1_mf_dmj045mz_.ctl         /u02/oracle/oradata/OEMDB/o1_mf_dmj045mz_.ctl
 # cp  /csrprd/datafile/OEMDB/o1_mf_dmj045o9_.ctl  /u02/oracle/oradata/OEMDB/o1_mf_dmj045o9_.ctl 
SQL> startup mount

Using RMAN, Switch the database to the Image Copy Backup:
RMAN> switch database to copy;

Copy the REDOLOG files to the new location:
SQL>  select 'cp '||member||'  /u02/oracle/oradata/OEMDB/'||SUBSTR(member, INSTR(member,'/', -1,1)+1) from v$logfile;
 # cp /u02/oracle/oradata/OEMDB/onlinelog/o1_mf_3_dmj0483s_.log  /csrprd/datafile/OEMDB/
 ...

-- Prepare and Execute the commands for renaming the REDOLOG files:
--prepare the script:
SQL> select 'ALTER DATABASE RENAME FILE '''||MEMBER||''' TO ''/u02/oracle/oradata/OEMDB/'||SUBSTR(member, INSTR(member,'/', -1,1)+1)||''' ;' from v$logfile ORDER BY 1;
--Execute:
SQL> ALTER DATABASE RENAME FILE '/csrprd/datafile/OEMDB/o1_mf_1_dmj0480w_.log'     TO '/u02/oracle/oradata/OEMDB/o1_mf_1_dmj0480w_.log' ;
...

RMAN> recover database;
RMAN> alter database open;

Re-create the Temporary tablespaces:
SQL> create temporary tablespace temp1 tempfile '/u02/oracle/oradata/OEMDB/temp1.dbf' size 10m;

SQL> alter database default temporary tablespace temp1;

-- Check & kill the sessions that still using the old TEMP tablespace:
set lines 170 pages 1000
      col sid_serial for a15
      col OSUSER for a20
      col module for a30
      col SPID for a10
      col TABLESPACE for a15
      select s.sid || ',' || s.serial# sid_serial, p.spid, s.username, s.osuser,
      s.module,sum (o.blocks) * t.block_size / 1024 / 1024 mb_used ,count(*) sorts, s.sql_id,o.tablespace
      from v$sort_usage o, v$session s, dba_tablespaces t, v$process p
      where o.session_addr = s.saddr and s.paddr = p.addr and o.tablespace = t.tablespace_name
      group by s.sid, s.serial#, s.username, s.osuser, p.spid, s.module,s.program, t.block_size,s.sql_id, o.tablespace
      order by o.tablespace,mb_used,s.username;

SQL> drop tablespace temp including contents and datafiles;
SQL> alter tablespace temp1 rename to temp;

Make sure that All DB files are pointing to the new location:
SQL> select name from v$datafile
union
select name from v$tempfile
union
select member from v$logfile
union
select name from v$controlfile;

SQL> sho parameter dest

Well done. The database is now migrated to the new location.

All About Statspack

Are DBAs still using Statspack instead of AWR? Yes
Why?

- It's the only option for Standby databases as you cannot genereate an AWR report on the Standby DB because the data will represent the Primary DB not the DR [Technical details are mentioned in the second half of this article]

- AWR is costly, if you use it one time you need to pay for the Diagnostic Pack license which is not cheap for small and mid-size companies.
https://www.oracle.com/assets/technology-price-list-070617.pdf

- Statspack is 80% of the AWR except it doesn't include the information about clusterware and other minor components, but still it can do 80% of the job for you.

- Although Statspack doesn't have the fancy HTML look of the AWR, but this shouldn't make you unsatisfied if you are an expert DBA 😅

- AWR is not available in Standard Edition, yes you can generate an AWR report on Standard Edition but it will be showing a useless report with zero values, then Oracle Licensing Audit Department will ask you to upgrade to Enterprise Edition because you tried to generate an AWR report on a SE, it sounds silly but this actually happened to me!

Conclusion, if you will buy the Diagnostic Pack license mainly for AWRs, I believe you will be paying too much just for the 20% of the information which is only available in AWR reports.

Now let's jump to the technical part which suppose to cover most of technical challenges when you deal with Statspack.


Statspack Installation:

Prerequisites:
- Although you can use SYSAUX tablespace to store statspack data, it's recommended to create a separate tablespace dedicated only for STATSPACK, e.g. TOOLS tablespace.
- For better performance analysis set the dynamic initialization parameter "timed_statistics" to true [Default].
ALTER SYSTEM SET timed_statistics=TRUE;

STATSPACK Installation Steps:

SQL>  connect / as sysdba
SQL>  @?/rdbms/admin/spcreate
PERFSTAT password:
default tablespace: assign "TOOLS" as a default tablespace for PERFSTAT
temporary tablespace:

SQL>  connect / as sysdba
SQL>  grant create job to PERFSTAT;

To take a snapshot manually: [For reference]
SQL>  connect perfstat/perf#123SQL>  execute statspack.snap;

Disable AWR Auto Snapshots: [Optional]
SQL> execute dbms_workload_repository.modify_snapshot_settings (   interval => 0,   retention => 43200);
Create a job to take a snapshot every 30min:
SQL>
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'STATSPACK_SNAP',
job_type => 'STORED_PROCEDURE',
job_action => 'statspack.snap',
repeat_interval => 'FREQ=HOURLY; BYHOUR=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23; BYMINUTE=4,34',
auto_drop => FALSE,
enabled => TRUE,
comments => 'Statspack automated snap');
END;
/

Create a job to Purge snapshotss older than one month:
-- Create the procedure:

SQL> Create or replace PROCEDURE purge_stats IS   loop_counter number;
   v_num number;
   tot_num number;
   CURSOR get_num IS
   SELECT round((count(*)/50),0) from stats$snapshot where snap_time < sysdate-31;
begin
   open get_num;
   FETCH get_num INTO tot_num;
   close get_num;
   tot_num := tot_num + 1;
   for loop_counter in 1 .. tot_num loop
       delete from stats$snapshot where snap_time < sysdate-31 and rownum <51;
       commit;
   end loop;
end;
/

-- Create the job:
SQL> BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'PURGE_STATSPACK_SNAP',
job_type => 'STORED_PROCEDURE',
job_action => 'purge_stats',
repeat_interval => 'FREQ=DAILY; BYHOUR=6; BYMINUTE=13',
auto_drop => FALSE,
enabled => TRUE,
comments => 'Purge Statspack snaps older than 31 days');
END;
/

Note: The time required to create a snapshot is dependent on the shared_pool_size,the larger the shared pool, the longer it will take to create a snapshot.

Generate a STATSPACK report:
SQL>  connect perfstat/perf#123
SQL>  @?/rdbms/admin/spreport

- You have to provide at least 2 snapshots IDs where the database didn't restart in between.
- It will ask for the report name.
- The report ill be created under the current working directory.

Advanced Tasks:


Mark a snapshot as a baseline

You can mark group of snapshots as a baseline represent your DB performance.
The snapshots inside the baseline will be kept and will not be deleted even by STATSPACK.PURGE.

Examples:

--This will make the snapshots from snap_id 45 to snap_id 50 in a baseline.
SQL> exec statspack.make_baseline (i_begin_snap => 45,i_end_snap => 50);

--This will make only snapshots 45,50 as a baseline.
SQL> exec statspack.make_baseline (45,50,false);

--This will take the snapshots between 9am to 12pm @31Aug.
SQL> exec statspack.make_baseline (
todate('31-AUG-2018 09:00','DD-MON-YYY HH24:MI'),
todate('31-AUG-2018 12:00','DD-MON-YYY HH24:MI');

--This will remove the baseline mark on the snapshots between 9am to 12pm and make them ordinary snapshots can be deleted:
SQL> exec statspack.clear_baseline (
todate('31-AUG-2018 09:00','DD-MON-YYY HH24:MI'),
todate('31-AUG-2018 12:00','DD-MON-YYY HH24:MI');


Manually Purge specific snapshots:
SQL>  connect perfstat/perf#123
SQL>  set transaction use rollback segment rbig;
SQL>  @?/rdbms/admin/sppurge

It will ask for the snapshot ID to begin with and the number of snapshots it will be purged after:
SQL>  connect perfstat/perf#123
SQL>  @?/rdbms/admin/sptrunc

Remove STATSPACK:
[AS SYSDBA]
SQL> @?/rdbms/admin/spdrop.sql
SQL> @?/rdbms/admin/sbdrop


STATSPACK for Standby DB:

As you know, you cannot generate neither an AWR nor a STATSPACK report directly on the standby database as the available statistics on the standby DB are a replica from the primary DB. Oracle recommends to install STATSPACK for standby on the primary DB itself, where performance stats will be gathered from the standby DB via DB link and will be stored on the Primary DB.

To install STATSPACK for a StandbyDB [orcldr]:
[FROM the Primary DB]
[AS SYSDBA]
SQL> @?/rdbms/admin/sbcreate
-- Provide a password for the stdbyuser user that will be created.
Default Tablespace: TOOLS
Temp Tablespace: TEMP

Do you want to continue (y/n) ?
Enter value for key: y
Enter value for tns_alias: orcldr1    -- STANDBY Instance TNS alias
Enter value for perfstat_password: -- Same passowrd

In case your Standby DB is a RAC setup, add the second STANDBY Instance2:
[FROM Primary DB]
SQL> connect stdbyperf/perf#123
SQL> @?/rdbms/admin/sbaddins
Enter value for tns_alias: orcldr2 -- STANDBY Instance2 TNS alias
Enter value for perfstat_password: -- Same passowrd

Create a job to automatically gather STATSPACK Snapshots from the Standby:
[FROM Primary DB]
SQL> conn / as sysdbaSQL> grant create job to stdbyperf;
SQL> connect stdbyperf/perf#123

SQL> BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'STATSPACK_SNAP_STANDBY_NODE1',
job_type => 'STORED_PROCEDURE',
job_action => 'STATSPACK_ORCLDR_ORCLDR1.snap',
repeat_interval => 'FREQ=HOURLY; BYHOUR=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23; BYMINUTE=4,34',
auto_drop => FALSE,
enabled => TRUE,
comments => 'Statspack automated snap for ORCLDR1');
END;
/

In case your Standby DB is a RAC setup, you have to create another job to create Snapshots for the second STANDBY Instance2:
[FROM Primary DB]
SQL> BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'STATSPACK_SNAP_STANDBY_NODE2',
job_type => 'STORED_PROCEDURE',
job_action => 'STATSPACK_ORCLDR_ORCLDR2.snap',
repeat_interval => 'FREQ=HOURLY; BYHOUR=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23; BYMINUTE=4,34',
auto_drop => FALSE,
enabled => TRUE,
comments => 'Statspack automated snap for ORCLDR2');
END;
/

Create a job to automatically purge old Snapshots:[Older then a month]

-- Create Purge Procedure to be called from purge job:
[FROM Primary DB]
SQL> Create or replace PROCEDURE purge_stats IS
   loop_counter number;
   v_num number;
   tot_num number;
   CURSOR get_num IS
   SELECT round((count(*)/50),0) from stats$snapshot where snap_time < sysdate-31;
begin
   open get_num;
   FETCH get_num INTO tot_num;
   close get_num;
   tot_num := tot_num + 1;
   for loop_counter in 1 .. tot_num loop
       delete from stats$snapshot where snap_time < sysdate-31 and rownum <51;
       commit;
   end loop;
end;
/

-- Create the job:
[FROM Primary DB]
SQL> BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'PURGE_STATSPACK_SNAP_STNDBY',
job_type => 'STORED_PROCEDURE',
job_action => 'purge_stats',
repeat_interval => 'FREQ=DAILY; BYHOUR=6; BYMINUTE=13',
auto_drop => FALSE,
enabled => TRUE,
comments => 'Purge Statspack snaps older than 31 days');
END;
/

Create a snapshot manually: [For reference]
[FROM Primary DB]
SQL> connect stdbyperf/perf#123
SQL> exec statspack_ORCLDR_ORCLDR1.snap --Create Snapshot for STANDBY Instance1
SQL> exec statspack_ORCLDR_ORCLDR2.snap --Create Snapshot for STANDBY Instance2

Generate a STATSPACK Report on the Standby DB:
[FROM Primary DB]
SQL> connect stdbyperf/perf#123
     @?/rdbms/admin/sbreport

To Purge Snapshots manually: [For reference][FROM Primary DB]
SQL> connect stdbyperf/perf#123
SQL>@?/rdbms/admin/sbpurge

Delete an instance from the STATSPACK configuration:
[FROM Primary DB]
SQL> connect stdbyperf/perf#123
SQL> @?/rdbms/admin/sbdelins

Tuesday, 25 September 2018

How To Find The Unused Indexes In Oracle Database

Getting rid of unused indexes on the database will not only free up space but it will boost the DML performance as well. But if you mistakenly get rid of a used index this may impact the performance. So let's go through the following steps which will let you do this task in a safe way.

Start With The Biggest Indexes In the Database:
I usually start this job by monitoring the biggest indexes in the database which can release much space and relief much of DML overhead on their underlying tables if those indexes got dropped.
This query will show the biggest 100 indexes:

set pages 200 lines 168
col owner for a40
col SEGMENT_NAME for a45
col TABLESPACE_NAME for a35
Select * from (select OWNER,SEGMENT_NAME,SEGMENT_TYPE,TABLESPACE_NAME,BYTES/1024/1024 SIZE_MB from dba_segments where SEGMENT_TYPE like 'INDEX%' order by 5 desc)where rownum <101 order by 5;


In order to get Oracle to monitor those indexes to figure out if they are used or not we should use this command:

ALTER INDEX <OWNER>.<INDEX_NAME> MONITORING USAGE;

Monitor The Biggest Indexes:
Now Let's enable monitoring on the biggest 100 indexes on the database using the output from this command:

set pages 200 lines 168
col Enable_Monitoring_Command for a100
Select * from (select 'ALTER INDEX '||OWNER||'.'||SEGMENT_NAME||' MONITORING USAGE;' Enable_Monitoring_Command,BYTES/1024/1024 SIZE_MB  from dba_segments where SEGMENT_TYPE like 'INDEX%' order by 2 desc) where rownum <101 order by 2 desc;

Executing the commands generated under "Enable_Monitoring_Command" will put the concerned indexes under monitoring.

Check the Monitored Indexes:
As a rule of thumb, you should keep those indexes under monitoring for the longest period as possible to avoid dropping an index that is being used somewhere in a weekly/monthly report.
From time to time, you have to check the monitored indexes that are reported as "USED" and remove them from the monitoring:

set linesize 200 pages 1000
col Index_NAME for a40
col TABLE_NAME for a40
col Monitored  for a9
col Used for a4
        select io.name Index_NAME, t.name TABLE_NAME,decode(bitand(i.flags, 65536),0,'NO','YES') Monitored,
        decode(bitand(ou.flags, 1),0,'NO','YES') USED,ou.start_monitoring,ou.end_monitoring
        from sys.obj$ io,sys.obj$ t,sys.ind$ i,sys.object_usage ou
where i.obj# = ou.obj# and io.obj# = ou.obj# and t.obj# = i.bo#
order by 3 desc,4;

To Un-Monitor indexes reported as "USED", use this command:

select 'ALTER INDEX <Index_owner>.'||io.name||' NOMONITORING USAGE;' INDEXES_IN_USE
from sys.obj$ io,sys.obj$ t,sys.ind$ i,sys.object_usage ou
where i.obj# = ou.obj# and io.obj# = ou.obj# and t.obj# = i.bo#
and decode(bitand(i.flags, 65536),0,'NO','YES')='YES' and decode(bitand(ou.flags, 1),0,'NO','YES')='YES' order by 1;

After keeping the rest of indexes under monitoring for a sufficent time (at least one month), do the following checks:

Make sure the "UNUSED" indexes are not associated with constraints:

SELECT OWNER,TABLE_NAME,CONSTRAINT_NAME,CONSTRAINT_TYPE,INDEX_NAME FROM DBA_CONSTRAINTS
WHERE INDEX_NAME IN (select io.name from sys.obj$ io,sys.obj$ t,sys.ind$ i,sys.object_usage ou
where i.obj# = ou.obj# and io.obj# = ou.obj# and t.obj# = i.bo#
and decode(bitand(i.flags, 65536),0,'NO','YES')='YES' and decode(bitand(ou.flags, 1),0,'NO','YES')='NO');

Check that UNUSED Indexes are not appearing in Exec Plans:
Make sure the "UNUSED" indexes are not used in any recent/old SQL Statements Execution plans:

select max(TIMESTAMP),object_name from v$SQL_PLAN
where OBJECT_NAME in ('<INDEX_NAME>')
group by object_name;

select max(TIMESTAMP),object_name from DBA_HIST_SQL_PLAN
where OBJECT_NAME in ('<INDEX_NAME>')
group by object_name;

Make sure the "UNUSED" indexes are not used in any recent SQL Statements Execution plans:

Set the UNUSED Index into INVISIBLE mode for sometime:
After making sure that the "UNUSED" indexes are not associated with any constraints and they are not used anymore in execution plans, you can put them into the INVISIBLE mode for some time:

 select 'ALTER INDEX P_FZ.'||io.name||' INVISIBLE;' SET_INVISIBLE
 from sys.obj$ io,sys.obj$ t,sys.ind$ i,sys.object_usage ou
where i.obj# = ou.obj# and io.obj# = ou.obj# and t.obj# = i.bo#
and decode(bitand(i.flags, 65536),0,'NO','YES')='YES' and decode(bitand(ou.flags, 1),0,'NO','YES')='NO';

Setting indexes into INVISIBLE mode will make them invisible to the application and thus will not be used by any query, This is much safer and faster than dropping the index, in case you came to know that those indexes are still being used, you can easily set them back to VISIBLE mode rather than re-create them from scratch in case you dropped them. 

Lastly, Drop the UNUSED Indexes:
Once we are definitely sure that the "UNUSED" indexes are not used, then we can drop them.
This command will drop all UNUSED monitored indexes:

select 'DROP INDEX P_FZ.'||io.name||' ;' DROP_INDEX
from sys.obj$ io,sys.obj$ t,sys.ind$ i,sys.object_usage ou
where i.obj# = ou.obj# and io.obj# = ou.obj# and t.obj# = i.bo#
and decode(bitand(i.flags, 65536),0,'NO','YES')='YES' and decode(bitand(ou.flags,1),0,'NO','YES')='NO';


Sunday, 23 September 2018

How To Drop An Oracle Database (Complete Guide)

Dropping a database is considered as a rare/dangerous/easy task, because of this it's not well documented as I think.
In general, there are two ways of dropping a database, using dbca and the manual way.
I usually don't trust GUI tools which dbca is one of them, this is why I'll explain the manual way :-)

I'll explain the steps for dropping a STAND ALONE & RAC databases.

Before you drop any DB make sure you're 100% conscious, not in a middle of something else and you're not in a hurry :-)

Dropping a STANDALONE DB:
let's suppose the database name to be dropped is orcl.

If the database is registered with Grid Control:
Connect to Grid Control with an admin user (e.g. SYSMAN)
Targets -> Databases -> Select ORCL -> Remove

Restart the DB in "RESTRICT" "EXCLUSIVE" mode:

[By oracle]
export ORACLE_SID=orcl
sqlplus "/ as sysdba"
SQL> shu immediate;
SQL> startup mount exclusive restrict;
SQL> select INSTANCE_NAME,HOST_NAME from v$instance;

Drop the database from RMAN:
export ORACLE_SID=orcl
rman target /
Recovery Manager: Release 11.2.0.3.0 - Production on Wed Jan 10 10:19:48 2018

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.

connected to target database: ORCL (DBID=1220202490, not open)

RMAN> DROP DATABASE INCLUDING BACKUPS;
...
database name is "ORCL" and DBID is 1220202490
database dropped
RMAN> exit

Note: INCLUDING BACKUPS option will delete RMAN Backups/Datafiles & Controlfiles copies along with the Database files.

Remove the DB from ORACLE RESTART repository:
[By oracle]
srvctl remove database -d orcl 

Delete the database entry from /etc/oratab: [both Nodes]

# vi /etc/oratab
[Remove the similar line]
orcl:/u01/app/oracle/product/11.2.0/dbhome_1:N

[Optional] Delete the instance files from $ORACLE_HOME/dbs:

cd $ORACLE_HOME/dbs
ls -l *orcl*

[Optional] Remove database diag directory from $ORACLE_BASE/diag/rdbms:

adrci
ADR base = "/u01/app/oracle"

adrci> show homes rdbms
ADR Homes:
diag/rdbms/orcl/orcl

adrci> exit

cd /u01/app/oracle/diag/rdbms
rm -rf orcl


Dropping a RAC DB:

let's suppose the database name to be dropped is orcl.

Remove the database from Grid/Cloud Control if it's already registered with it:
Connect to Grid Control with an admin user (e.g. SYSMAN)
Targets -> Databases -> Select ORCL -> Remove

Set the cluster_database parameter to false:
SQL> alter system set cluster_database=false scope=spfile;

Shutdown the database on ALL RAC nodes:
srvctl stop database -d orcl

Startup the DB in "RESTRICT" "EXCLUSIVE" mode: from only one node:

[By oracle]
export ORACLE_SID=orcl1
sqlplus "/ as sysdba"
SQL> startup mount exclusive restrict;
SQL> select INSTANCE_NAME,HOST_NAME from v$instance;

Drop the database:
-----------------
export ORACLE_SID=orcl1
# rman target /
Recovery Manager: Release 11.2.0.3.0 - Production on Wed Jan 10 10:17:42 2018

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.

connected to target database: ORCL (DBID=1675848900, not open)

RMAN> DROP DATABASE INCLUDING BACKUPS;
...
database name is "ORCL" and DBID is 1675848900
database dropped
RMAN> exit

Remove the database from the Clusterware configurations:
[By oracle]
srvctl remove database -d orcl

Delete the database & instance entry from /etc/oratab: [Both RAC nodes]
vi /etc/oratab
orcl1:/u01/app/oracle/product/11.2.0/db_1:N # line added by Agent
orcl:/u01/app/oracle/product/11.2.0/db_1:N # line added by Agent

[Optional] Delete instance files from $ORACLE_HOME/dbs: [Both RAC nodes]
cd $ORACLE_HOME/dbs
ls -l *orcl*

[Optional] Remove database diag directory from $ORACLE_BASE/diag/rdbms:  [Both RAC nodes]
adrci
ADR base = "/u01/app/oracle"

adrci> show homes rdbms
ADR Homes:
diag/rdbms/orcl/orcl1
diag/rdbms/orcl/orcl
diag/rdbms/test/test
diag/rdbms/test/test1
diag/rdbms/_mgmtdb/-MGMTDB

adrci> exit

# cd /u01/app/oracle/diag/rdbms
# rm -rf orcl

Monday, 25 August 2014

Recover Data from Damaged/Corrupted/Inconsistent/Offline/Truncated/Dropped Table While the Database is Offline

When it comes to catastrophic scenarios where the database has been corrupted and there is no available backup, flashback feature was not turned on, even when database cannot be open due to errors or inconsistent status, there is still a chance to recover and save the data as long as the datafiles are still exist using third party tools that can directly read from datafiles without the need to open the database.
In this post I'll discuss one of these tools, Oracle Database Unloader (ODU).

Download ODU:
==============
http://www.oracleodu.com/en/download

Full version: It restores all data only if you got the license file from the support [Not Free].
Trial version: It doesn't need license but it's limited, as it doesn't work on ASM, it can recover all data under system datafiles but for other datafiles it will recover only less than 1% of all rows.

Note: In this demonstration I've used the TRIAL version, this is why I was restoring tables located under system tablespace only to be able to recover all data.

Install ODU:
===========
Upload the downloaded file to the server and then extract it:

# gunzip odu_433_linux_x86.tar.gz 
# tar xvf odu_433_linux_x86.tar 
# cd odu
# chown u+x odu

Prepare ODU:
============
Before start using ODU for recovering data, first you have to modify the configuration files to point to the right path of datafiles.

In case the datafiles stored on ASM modify asmdisk.txt by adding ASM disks path to asmdisk.txt file:

# vi asmdisk.txt
0 /oradata/asm/disk1.dbf 
0 /oradata/asm/disk2.dbf 
0 /oradata/asm/disk3.dbf 

Note: The first column should be filled with 0

In case the datafiles are not stored on ASM, add all datafiles path to control.txt file:
This SQL can help you generate the lines you will add to control.txt :
SQL> SELECT 0,0,0,NAME FROM V$DATAFILE ORDER BY FILE#;

# vi control.txt
0 0 0 /database/orcl/datafiles/system01.dbf
0 0 0 /database/orcl/datafiles/users01.dbf
0 0 0 /database/orcl/datafiles/sysaux01.dbf
0 0 0 /database/orcl/datafiles/undotbs01.dbf
0 0 0 /database/orcl/datafiles/example01.dbf

Note:  The first three columns should be filled with 0

The rest of ODU configuration files like config.txt, contains configurations like block_size which must be the same for the database you are extracting data from.
In config.txt the parameter output_format represent the fashion where the data will be saved after extracting (unload) it from tables. the default value is text where data will be saved in SQL LOADER format, the other value is dump where data will be saved in export dump format.

In case you are using the full version, arrange for the license by save the configuration in a file and send it to ODU support to send you back the license file:
Save the controls to oductl.txt:
# ./odu
ODU> save control 
ODU> exit 

Send the file oductl.txt to the ODU support mailbox in http://www.oracleodu.com/en/support, they will send you the license file named oductl.dat, copy it under ODU installation directory.

* Note that Trial version doesn't need a license but it's data restoration capability is limited as mentioned before.

Using ODU:
==========

Discover the database:
>>>>>>>>>>>>>>>>>>>>
# ./odu

ODU> load config [filename] #Not mandatory, if file name ommited it will load config.txt file
ODU> open [filename] #Not mandatory, if file name ommited it will load control.txt file

ODU> unload dict         #Load dictionary data from SYSTEM tablespace.
ODU> list user         #List database users & roles.
ODU> desc scott.emp #descripe a table plus provide extra data like object id, tablespace,..etc.

Restore data of a truncated table: [TEST SCENARIO]
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
*control.txt file must be loaded with datafiles location as shown before.

SQL> CREATE TABLE SCOTT.TEST TABLESPACE USERS AS SELECT * FROM DBA_USERS;
SQL> SELECT COUNT(*) FROM SCOTT.TEST;
SQL> TRUNCATE TABLE SCOTT.TEST;
SQL> alter system checkpoint;
SQL> alter tablespace users offline; #important/not mandatory to offline the tablespace of truncated table to avoid space overwrite.
SQL> alter system checkpoint; #important to write changes to the disk or may ODU will not find the table.

# ./odu

ODU> load config [filename] #Not mandatory, if file name ommited it will load config.txt file
ODU> open [filename] #Not mandatory, if file name ommited it will load control.txt file

ODU> unload dict         #Load dictionary data from SYSTEM tablespace.
ODU> desc scott.test #Get information of truncated table location (check it's tablespace number: TS#=<> )
Storage(Obj#=55125 DataObj#=55126 TS#=4 File#=4 Block#=403 Cluster=0)

ODU> scan extent tablespace 4 #Scan the extents of the tablespace # of the truncated table.
ODU> unload table scott.test object truncate #Restore all rows of truncated table in a SQL LOADER file.

SQL> alter tablespace users online; #Online back the tablespace of truncated to load back truncated data.

Use SQL LOADER to load the data back to the truncated table:
# cd <odu location>/data
# sqlldr scott/tiger control=SCOTT_TEST.ctl 

Note: In case you want to restore the full data on a table not located on system tablespace, you must use the full licensed version of ODU.

Restore a Dropped Table:
>>>>>>>>>>>>>>>>>>>>>>>
Let's create and drop a table for testing:

SQL> create table scott.bb tablespace system as select * from dba_synonyms;
SQL> drop table scott.bb;
SQL> alter system checkpoint;

Before using ODU the first task to do is to get the OBJECT ID for the dropped table using Log Miner feature in Oracle:
--Add redo/archive that contain the drop transaction:
SQL> EXEC sys.dbms_logmnr.add_logfile(logfilename=>'/u01/app/oracle/oradata/xty/redo03.log'); 
--Start mining the registered archives:
SQL> EXEC sys.dbms_logmnr.start_logmnr(options=>sys.dbms_logmnr.dict_from_online_catalog);    
--Get the execution time of the drop statement:
SQL> SELECT scn,timestamp,sql_redo from v$logmnr_contents where operation='DDL' and sql_redo like '%drop%' order by 2 ; 
--Use the previous execution time to get the internal delete statement of the dropped table from the dictionary, where the object id can found:
SQL> SELECT scn,timestamp,sql_redo from v$logmnr_contents
     where timestamp=to_date('2014-05-21 10:33:33','yyyy-mm-dd hh24:mi:ss') and sql_redo like '%OBJ%' order by 1;
--End the Log Miner task:
SQL> EXEC sys.dbms_logmnr.end_logmnr; 

Now we suppose to have the object id of the dropped table.

In case you're just testing and don't want to go through this leghnthy process before dropping the table just get it's object id from ODU:
# ./odu
ODU> unload dict
ODU> desc scott.bb #you'll find the object id in the output.

Start the table restoration job using ODU:
# ./odu
ODU> unload dict #Load dictionary data from SYSTEM tablespace.
ODU> scan extent tablespace 0 #scan extents of tablespace 0 (system)
ODU> unload object all tablespace 0 sample #This will provide you with the command you shall use to restore for EVERY object inside tablespace 0
ODU> unload user scott #Unload ALL tables owned by user SCOTT (each table will have 3 files located under data directory).
ODU> unload object 55150 sample #This will provide you with the command you shall use to restore for SPECIFIC object inside tablespace 0.
ODU> unload object 55150 tablespace 0 column VARCHAR2 VARCHAR2 VARCHAR2 VARCHAR2 #Just copy & execute the command from the previous output.

Note: unload command will generate three files for each unloaded table ".txt",".sql",".ctl"

From OS side modify file <odu location>/data/ODU_0000055150.sql> by changing the name of object from ODU_0000055150 to SCOTT.BB and execute it:
SQL> @<odu location>/data/ODU_0000055150.sql>
Table created.

From OS modify file <odu location>/data/ODU_0000055150.ctl> change the name of object from ODU_0000055150 to SCOTT.BB and start load the table with data using SQLLOADER:
# sqlldr scott/tiger control=ODU_0000055150.ctl


Export Data from an existing Table: [Database is down / Skipping Auditing/Permissions]
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# ./odu
ODU> unload dict #Load dictionary data from SYSTEM tablespace.
ODU> desc scott.bb #Scan the extents of the tablespace number of the truncated table.
ODU> scan extent tablespace 0 #Scan the extents of the tablespace #.
ODU> unload table scott.bb object scanned #Export data of table SYS.AA into SQL LOADER file. [object scanned] is not mandatory.

Open the .sql file and change the table owner&name if you wish, do the same for .ctl file then use SQLLOADER to import the data.
# sqlldr scott/tiger control=SCOTT_AA.ctl


Conclusion:
ODU tool can help restoring data in a catastrophic scenarios where there is no available backup or database has damaged or cannot be opened due to any reason,
on the other hand this tool prove that database security can be easily compromised by anyone have access to database files and have a basic knowledge in using such tools, in order to avoid such breach the DBA should consider encrypting the data on filesystem level using an encryption feature such as TDE, also security hardening and limiting the access on the operating system level should be considered to prevent un-authorized users from accessing the server or the file system storage.

REFERENCE:
Full documentation: http://www.oracleodu.com/soft/ODUUserGuide_en.pdf
Real success story: http://www.basecare.dk/6storage/983/7/recovering_lob-index_block_corruption.pdf