Showing posts with label Oracle. Show all posts
Showing posts with label Oracle. Show all posts

2012-04-24

DBMS_REDEFINITION sucks if you have CLOBs

DBMS_REDEFINITION is a well-known, built-in package of Oracle, which can be used to reorganize tables. It works well most of the times, but recently I have found myself working out a custom solution to reorg a large table for a customer.

For the impatient: if your table you are going to reorg contains CLOB data, you might have to consider using an alternative method such as CTAS. (Problem appeared on 11.2.0.2, I did not test it on other Oracle versions).

In this case our customer had a trace table which occupied approximately 27GB (20GB of that belonged to LOB segments!). In order to sort the problem out, the proposed solution was that we would re-create it as a partitioned table and Oracle's secure file feature with compression would be used to help cut down on required space.

For some reason the execution of DBMS_REDEFINITION.SYNC_INTERIM_TABLE(...) has never finished and the whole process got stuck in this step. As the name of the procedure suggests this ought to synchronize inserted/updated/deleted records from the original table to the new one. Internally DBMS_REDEFINITION uses a materialized view and a materialized view log on the source table to do this job.

Most likely the CLOB column which the source table had, was responsible for the misbehaviour, I experienced.

You can reproduce this issue easily by taking the following steps.
  1. Create a large enough source table and an empty destination table with the same definition.

  2. -- Create source table
    create table old_table
    as select ao.owner, ao.object_name from all_objects ao where 1 = 2;

    -- Add columns (if you change the DATA column to VARCHAR2,
    -- or you simply just remove it, everything will be working
    -- fine.
    alter table old_table add data clob not null;
    alter table old_table add id number not null;

    create table new_table
    (
      owner          varchar2(30 byte) not null,
      object_name    varchar2(30 byte) not null,
      data           clob,
      id             number
    );
    -- Populate original table (it inserts ~1M records on my database)
    create sequence seq minvalue 1 start with 1 increment by 1 cache 10;
    insert into old_table
      select ao.owner, ao.object_name, ao.owner || '.' || ao.object_name as data, seq.nextval as data from all_objects ao, dba_users du
      where du.username like '%SYS%';
    commit;

    -- We have to add a primary key, because redefinition
    -- will not start without a primary key.
    alter table old_table add constraint pk_old_table primary key (id);

  3. Perform on-line redefinition

  4. declare
      procedure cleanup is
      begin
        dbms_application_info.set_action(action_name => '');
        dbms_monitor.session_trace_disable();
      end;
    begin
      dbms_monitor.session_trace_enable(waits => true, binds => true);
      dbms_application_info.set_action(action_name => 'start_redef_table');
      dbms_redefinition.start_redef_table( uname => 'lcsontos',
                                           orig_table => 'old_table',
                                           int_table => 'new_table');
      dbms_application_info.set_action(action_name => 'sync_interim_table');
      dbms_redefinition.sync_interim_table( uname => 'lcsontos',
                                            orig_table => 'old_table',
                                            int_table => 'new_table');
      dbms_application_info.set_action(action_name => 'finish_redef_table');
      dbms_redefinition.finish_redef_table( uname => 'lcsontos',
                                            orig_table => 'old_table',
                                            int_table => 'new_table');
      cleanup();
    exception
      when others then
        cleanup();
        dbms_redefinition.abort_redef_table( uname => 'lcsontos',
                                             orig_table => 'old_table',
                                             int_table => 'new_table');
        dbms_output.put_line(dbms_utility.format_error_backtrace());
    end;

  5. While redefinition is in progress insert some records to the source table

  6. insert into old_table
      select ao.owner, ao.object_name, ao.owner || '.' || ao.object_name, seq.nextval as data from all_objects ao
      where rownum <= 1000;
    commit;

  7. Your process will be bogged down on the sync phrase.

  8. Take a look at the active sessions, this query will be running (and running ...), until you kill it. If you try to execute it from another session it gives a result within a few seconds. So I suppose there is nothing wrong with the query itself.

    SELECT CURRENT$."OWNER",
           CURRENT$."OBJECT_NAME",
           CURRENT$."SUBOBJECT_NAME",
           CURRENT$."OBJECT_ID",
           CURRENT$."DATA_OBJECT_ID",
           CURRENT$."OBJECT_TYPE",
           CURRENT$."CREATED",
           CURRENT$."LAST_DDL_TIME",
           CURRENT$."TIMESTAMP",
           CURRENT$."STATUS",
           CURRENT$."TEMPORARY",
           CURRENT$."GENERATED",
           CURRENT$."SECONDARY",
           CURRENT$."NAMESPACE",
           CURRENT$."EDITION_NAME",
           CURRENT$."DATA",
           CURRENT$."ID",
           LOG$.CHANGE_VECTOR$$
      FROM (SELECT "OLD_TABLE"."OWNER"          "OWNER",
                   "OLD_TABLE"."OBJECT_NAME"    "OBJECT_NAME",
                   "OLD_TABLE"."SUBOBJECT_NAME" "SUBOBJECT_NAME",
                   "OLD_TABLE"."OBJECT_ID"      "OBJECT_ID",
                   "OLD_TABLE"."DATA_OBJECT_ID" "DATA_OBJECT_ID",
                   "OLD_TABLE"."OBJECT_TYPE"    "OBJECT_TYPE",
                   "OLD_TABLE"."CREATED"        "CREATED",
                   "OLD_TABLE"."LAST_DDL_TIME"  "LAST_DDL_TIME",
                   "OLD_TABLE"."TIMESTAMP"      "TIMESTAMP",
                   "OLD_TABLE"."STATUS"         "STATUS",
                   "OLD_TABLE"."TEMPORARY"      "TEMPORARY",
                   "OLD_TABLE"."GENERATED"      "GENERATED",
                   "OLD_TABLE"."SECONDARY"      "SECONDARY",
                   "OLD_TABLE"."NAMESPACE"      "NAMESPACE",
                   "OLD_TABLE"."EDITION_NAME"   "EDITION_NAME",
                   "OLD_TABLE"."DATA"           "DATA",
                   "OLD_TABLE"."ID"             "ID"
              FROM "LCSONTOS"."OLD_TABLE" "OLD_TABLE") CURRENT$,
           (SELECT MLOG$."ID",
                   SYS.MVAggRawBitOr(MLOG$.CHANGE_VECTOR$$) CHANGE_VECTOR$$
              FROM "LCSONTOS"."MLOG$_OLD_TABLE" MLOG$
             WHERE "SNAPTIME$$" > :1
               AND ("DMLTYPE$$" != 'D')
             GROUP BY MLOG$."ID") LOG$
     WHERE CURRENT$."ID" = LOG$."ID"

    I have also traced it, but looking at the results did not give me any clue.

    Elapsed times include waiting on following events:
      Event waited on                             Times   Max. Wait  Total Waited
      ----------------------------------------   Waited  ----------  ------------
      db file sequential read                         2        0.03          0.04

    Yes, it is waiting for sequential read exactly the same way as it did in my case when I was on-site.
Obviously we hit a bug, but unfortunately I did not find anything similar which comes close to this issue on Metalink. I was lucky, because the table I had to reorg, is being only inserted, so I created a log table with an insert trigger in order to catch new records while CTAS was running. After that my script added those newly inserted records and created necessary dependant objects (indices, keys, etc.) and collected statistics.

2010-03-16

Oracle database silent installation on Debian Lenny (Part 2)

This is the continuation of my previous post, I'll install Oracle RDBMS 11gR2 in this part.

A lot of steps can be skipped at this time, because installation will be taken place on the same machine and Oracle RDBMS 10gR2 has already been successfully deployed.

Change the current directory on that one to where Oracle install kit has been extracted.

$ cd /path/to/rdbms112_install

Create a response file.

$ vi rdbms111.rsp
oracle.install.responseFileVersion=/oracle/install/rspfmt_dbinstall_response_schema_v11_2_0
oracle.install.option=INSTALL_DB_SWONLY
UNIX_GROUP_NAME=dba
INVENTORY_LOCATION=/usr/local/oracle/inventory
SELECTED_LANGUAGES=en
ORACLE_HOME=/usr/local/oracle/product/rdbms112
ORACLE_BASE=/usr/local/oracle
oracle.install.db.InstallEdition=EE
oracle.install.db.isCustomInstall=false
oracle.install.db.DBA_GROUP=dba
oracle.install.db.OPER_GROUP=dba
SECURITY_UPDATES_VIA_MYORACLESUPPORT=false
DECLINE_SECURITY_UPDATES=true

Start Oracle Universal Installer. (Note that swith -ignoreSysPrereqs has been changed to -ignorePrereq as of this release)

$ ./runInstaller -ignorePrereq -responseFile /path/to/rdbms111.rsp -silent

Execute the plain old root.sh.

# /usr/local/oracle/product/rdbms112/root.sh

2010-02-21

Oracle database silent installation on Debian Lenny (Part 1)

This is a short write-off about only the absolutely necessary steps how to deploy Oracle 10gR2 on a box running debian lenny.

Check out the packages need to be installed here.

I usually only set up those kernel parameter that have lower default values than recommended by ORACLE.

# echo "kernel.sem = 250 32000 100 128" >> /etc/sysctl.conf
# echo "kernel.shmmax = 1073741824" >> /etc/sysctl.conf
# sysctl -p

Create dba group and oracle user (groups oper and oinstall can be safetly omitted, I've been never using them)

# useradd -m -d /home/oracle -g dba -s /bin/bash oracle
# groupadd dba

Create directories for software. In the majority of articles dealing with Oracle RDBMS installation /u01, /02, etc. are used, but I prefer to put it into somewhere in the standard directory layout.

# mkdir -p /usr/local/oracle/inventory
# mkdir -p /usr/local/oracle/network/admin
# mkdir -p /usr/local/oracle/product/rdbms102

Directories for data files, trace files and for flashback recovery area.

# mkdir -p /var/local/oracle/oradata
# mkdir -p /var/local/oracle/fra
# mkdir -p /var/local/oracle/admin

Let user oracle to take possession of the directories created above.

# chown -R oracle:dba /usr/local/oracle
# chown -R oracle:dba /var/local/oracle

Mark the location of Oracle software inventory. This step is optional universal installer will ask you to define it if you skip this step.

# cat > /etc/oraInst.loc <<EOF
inventory_loc=/usr/local/oracle/inventory
inst_group=dba
EOF
# chown oracle:dba /etc/oraInst.loc

Log in as oracle and change the working directory to that location where the installation package was extracted.
$ cd /path/to/rdbms102_install
I like silent install because this way I don't have to care for X window system.

$ vi rdbms102.rsp
RESPONSEFILE_VERSION=2.2.1.0.0
UNIX_GROUP_NAME="dba"
FROM_LOCATION="../stage/products.xml"
ORACLE_HOME="/usr/local/oracle/product/rdbms102"
ORACLE_HOME_NAME="rdbms102"
ACCEPT_LICENSE_AGREEMENT=true
INSTALL_TYPE="EE"
NEXT_SESSION=false
s_nameForDBAGrp=dba
s_nameForOPERGrp=dba
n_configurationOption=3

As a final step run the installer.

$ ./runInstaller -ignoreSysPrereqs -responseFile /path/to/rdbms102.rsp -silent

Execute the plain old root.sh.

# /usr/local/oracle/product/rdbms102/root.sh

2009-06-24

Clone Oracle AS 10gR3 home for patching on Ubuntu 8.04 LTS

I've got on Oracle As 10gR3 installed on my laptop. It really isn't my favourite application server, yet I have to use it every day in the course of my work

I have a base on version (10.1.3.1) on that box and this article is about to show how to clone, patch and replace the original JVM of an Oracle AS home.

Logged in as oracle.

$ id -a
uid=1001(oracle) gid=1001(oinstall) groups=1001(oinstall),1002(dba)


List of the non-RDBMS Oracle homes on the machine.

$ egrep -v '^$|^#' /etc/oratab | grep '^*'
*AS1:/usr/lib/oracle/app/oracle/product/10.1.3.1/as_1:N
*MRCA:/usr/lib/oracle/app/oracle/product/10.1.4.0/mrca:N
*CLN:/usr/lib/oracle/app/oracle/product/10.2.0/client_1:N


Select the appropriate Oracle home and check the environment before proceeding to patching.

$ . oraenv
ORACLE_SID = [*AS1] ?

$ env | grep -i ora
USER=oracle
LD_LIBRARY_PATH=/usr/lib/oracle/app/oracle/product/10.1.3.1/as_1/lib
ORACLE_SID=*AS1
USERNAME=oracle
TNS_ADMIN=/home/oracle/network/admin
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/usr/lib/oracle/app/oracle/product/10.1.3.1/as_1/bin
PWD=/home/oracle
HOME=/home/oracle
LOGNAME=oracle
ORACLE_HOME=/usr/lib/oracle/app/oracle/product/10.1.3.1/as_1


Check perl where it is and what's the version of it.

$ which perl
/usr/bin/perl

$ perl -v

This is perl, v5.8.8 built for i486-linux-gnu-thread-multi

Copyright 1987-2006, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl". If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.


Cloning an Oracle AS home is quite easy, first I prepare the current home (the process is further detailed in Oracle AS's admin guide)

$ perl $ORACLE_HOME/clone/bin/prepare_clone.pl ORACLE_HOME=$ORACLE_HOME
Clone log file location: /usr/lib/oracle/app/oracle/product/10.1.3.1/as_1/clone/logs/clone1245401177.log
Error log file location: /usr/lib/oracle/app/oracle/product/10.1.3.1/as_1/clone/logs/error1245401177.log
Fri Jun 19 10:46:17 2009 INFO: Starting with the Prepare Clone operation at the source
Fri Jun 19 10:46:17 2009 INFO: The temp directory being used is /tmp
Fri Jun 19 10:46:17 2009 INFO: The prepare clone operation completed successfully.


After the preparation finished copy the original Oracle home as it is and do the cloning.

$ cp -Rpv $ORACLE_HOME $ORACLE_HOME/../as_2 &< copy.log
$ export ORACLE_HOME=/usr/lib/oracle/app/oracle/product/10.1.3.1/as_2
$ perl $ORACLE_HOME/clone/bin/clone.pl ORACLE_HOME=$ORACLE_HOME ORACLE_HOME_NAME=oracleas2 -instance as_2 -oc4jadmin_old_password <old_pwd> -oc4jadmin_new_password <new_pwd>


Execute root.sh since it always has to be done in situations like this.

# /usr/lib/oracle/app/oracle/product/10.1.3.1/as_2/root.sh

Registering the newly created Oracle AS home must be done manually.

$ vim /etc/oratab
...
*AS1:/usr/lib/oracle/app/oracle/product/10.1.3.1/as_1:N
*AS2:/usr/lib/oracle/app/oracle/product/10.1.3.1/as_2:N
...


Oracle AS patchset installation is coming which is very common, so I'm not going to specify it in-depth.

Tell OPMN to shut all running managed processes down.

$ . oraenv
ORACLE_SID = [*AS1] ? *AS2
$ $ORACLE_HOME/opmn/bin/opmnctl shutdown
opmnctl: shutting down opmn and all managed processes...


Unpack the patch set and launch the installer.

$ mkdir -p ~/install/ias_patch3
$ cd ~/install/ias_patch3
$ unzip /path/to/Downloads/p7272722_101340_LINUX.zip
$ cd Disk1
$ ./runInstaller -ignoreSysPrereqs