Tuesday, February 18, 2014

Partitioning of tables & indexes

Part 1: Partitioning of Tables:


Introduction:

Partitioning addresses in supporting very large tables and indexes by letting you decompose into small and more managable pieces called "Partitions".
It is like concept devide and conquer which is very useful to the very large tables and indexes in VLDBs.
sql queries and DML statements do not need to be modified in order to access the partitioned tables.

All partitions of the partitioned object should reside on the tablespaces of same block size.

Tables can be partitioned into up to 64,000( till 10g 64K-1) (from 10g 1024K-1)  seperate partitions.Any table can ne partitioned except those tables containing columns with LONG or LONG RAW, however we can partition the tables with columns CLOBs and BLOBs.

When to partition a table:

Table greater than 2GB should always be considered for partitioning.
Tables contains historical data in which the new data is added into the newest partition. Example for historical table is where only the current month's data is updatable and other months data is read-only.

When performance benifits, the additional maintanance at the partition level is required.

Advanatages of partitiong:

1. Management at individual partition level for data loads,index creation and rebuilding, backup/recovery.
2. Concurrent operations can be performed on different partitions of the same table/index.
3.Parallel DML operations can be performed at partition level.This reduces the response time for data-intensive operations in VLDBs.you can use parallel query and parallel DML with range- and hash-partitioned tables.
4.Partition wise joining which brokes the smaller joins that are performed either in parallel or sequential.
5. Increased query performance by selecting only relavant partitions rather than entire table by using technique called "partition pruning".

Partition pruning:


The Oracle server explicitly recognizes partitions and subpartitions. It then optimizes SQL statements to mark the partitions or subpartitions that need to be accessed and eliminates (prunes) unnecessary partitions or subpartitions from access by those SQL statements.
In other words, partition pruning is the skipping of unnecessary index and data partitions or subpartitions in a query.
The optimizer cannot prune partitions if the SQL statement applies a function to the partitioning column (with the exception of the TO_DATE function). Similarly, the optimizer cannot use an index if the SQL statement applies a function to the indexed column, unless it is a function-based index

Partition key:

Partition key is set of one or more columns that determines the partition for each row.Oracle automatically directs insert/update/delete operations to the appropriate partitions through the use of partition key.

partition key can't contain a LEVEL,rowid, pseudo columns or a column of type rowid.

When the partition key value has been updated by sql, oracle auotmatically moves the row into appropriate partition using row movement.

If you want to select the data from perticular partiton:
SELECT * FROM supplier_parts PARTITION (p1);

Partition Methods:

Range partitions:  Used when there are logical ranges of data.
                               Possible usage: dates, part numbers, and serial numbers, salary ranges etc.

List partitinos:       Used to list together unrelated data into partitions.
                               Possible usage: a number of states list partitioned into a region.
Hash partitions:   - Used to spread data evenly over partitions.
                               Possible usage: data has no logical groupings.

Composite partitions: Is a combination of other partitioning methods
                            Ex: 1)Range-Hash : Used to range partition first, then spreads data into hash partitions.
                                  2) Range-List  : Used to range partition first, then spreads data into list partitions.


1. Range Partitions:

    This is first type of partition introduced by oracle with 9i version.Range partition maps the data to partitions based on the partition key values. It is most common type and often used with dates
Ex: might want to partition sales data into monthly partition.

When using range partition-
Each partition has cluase "VALUES LESS THAN" clause which specified the upper bound for that partition.
Any key values which is equal to or greater than this value will be directed into the next higher partition.
 All partitions (except the first) have an implicit lower bound specified by the "values less than" clause on previous partition.

The last partition is the "catch all" partition which has "MAXVALUE" cluase, this cluase represents virtual infinite value that sorts higher than any other possible values for partition key inclusing the NULL values.


EX:
  create table sales_range
(salesman_id  NUMBER(5), 
salesman_name VARCHAR2(30), 
sales_amount  NUMBER(10), 
sales_date    DATE)
PARTITION BY RANGE(sales_date) 
(
PARTITION sales_jan2000 VALUES LESS THAN(TO_DATE('02/01/2000','DD/MM/YYYY')),
PARTITION sales_feb2000 VALUES LESS THAN(TO_DATE('03/01/2000','DD/MM/YYYY')),
PARTITION sales_mar2000 VALUES LESS THAN(TO_DATE('04/01/2000','DD/MM/YYYY')),
PARTITION sales_apr2000 VALUES LESS THAN(TO_DATE('05/01/2000','DD/MM/YYYY')),
partition sales_max values less than MAXVALUE
);

2.List partitions:

List partitioning enables you to explicitly control how rows map to partitions. You do this by specifying a list of discrete values for the partitioning key in the description for each partition

The advantage of list partitioning is that you can group and organize unordered and unrelated sets of data in a natural way.

Unlike range and hash partitioning, multicolumn partition keys are not supported for list partitioning

Ex:

CREATE TABLE sales_list
(salesman_id  NUMBER(5), 
salesman_name VARCHAR2(30),
sales_state   VARCHAR2(20),
sales_amount  NUMBER(10), 
sales_date    DATE)
PARTITION BY LIST(sales_state)
(
PARTITION sales_west VALUES('California', 'Hawaii'),
PARTITION sales_east VALUES ('New York', 'Virginia', 'Florida'),
PARTITION sales_central VALUES('Texas', 'Illinois')
PARTITION sales_other VALUES(DEFAULT)
);

The DEFAULT partition enables you to avoid specifying all possible values for a list-partitioned table by using a default partition, so that all rows that do not map to any other partition do not generate an error.


3. Hash partitions:
  
Oracle's hash partitioning distributes data by applying a proprietary hashing algorithm to the partition key and then assigning the data to the appropriate partition. By using hash partitioning, DBAs can partition data that may not have any logical ranges. Also, DBAs do not have to know anything about the actual data itself. Oracle handles all of the distribution of data once the partition key is identified.

It is a better choice than range partitioning when:
The sizes of range partitions would differ quite substantially or would be difficult to balance manually.

NOTE: The concepts of splitting, dropping or merging partitions do not apply to hash partitions. Instead, hash partitions can be added and coalesced.


CREATE TABLE sales_hash
(salesman_id  NUMBER(5), 
salesman_name VARCHAR2(30), 
sales_amount  NUMBER(10), 
week_no       NUMBER(2)) 
PARTITION BY HASH(salesman_id) 
PARTITIONS 4 
STORE IN (data1, data2, data3, data4);

This statement creates 4 partitions with default naming convention in 4 diff tablespaces.



4. Composite patition (Sub-partition):

Composite partitioning partitions data using the range method, and within each 
partition, subpartitions it using the hash or list method.

Composite partitioning supports historical operations, such as adding new range partitions, but also provides higher degrees of parallelism for DML operations and finer granularity of data placement through subpartitioning.
Composite range-hash partitioning combines both the ease of range partitioning and the benefits of hashing for data placement, striping, and parallelism.
Ex: Range-hash partition
CREATE TABLE sales_composite 
(salesman_id  NUMBER(5), 
 salesman_name VARCHAR2(30), 
 sales_amount  NUMBER(10), 
 sales_date    DATE)
PARTITION BY RANGE(sales_date) 
SUBPARTITION BY HASH(salesman_id)
SUBPARTITION TEMPLATE(
SUBPARTITION sp1 TABLESPACE data1,
SUBPARTITION sp2 TABLESPACE data2,
SUBPARTITION sp3 TABLESPACE data3,
SUBPARTITION sp4 TABLESPACE data4)
(PARTITION sales_jan2000 VALUES LESS THAN(TO_DATE('02/01/2000','DD/MM/YYYY'))
 PARTITION sales_feb2000 VALUES LESS THAN(TO_DATE('03/01/2000','DD/MM/YYYY'))
 PARTITION sales_mar2000 VALUES LESS THAN(TO_DATE('04/01/2000','DD/MM/YYYY'))
 PARTITION sales_apr2000 VALUES LESS THAN(TO_DATE('05/01/2000','DD/MM/YYYY'))
 PARTITION sales_may2000 VALUES LESS THAN(TO_DATE('06/01/2000','DD/MM/YYYY')));

Composite range-list partitioning combines both the ease of range partitioning 
and the benefits of list partitioning at the subpartition level.
Ex: Range-list partition

CREATE TABLE bimonthly_regional_sales
(deptno NUMBER, 
 item_no VARCHAR2(20),
 txn_date DATE, 
 txn_amount NUMBER, 
 state VARCHAR2(2))
PARTITION BY RANGE (txn_date)
SUBPARTITION BY LIST (state)
SUBPARTITION TEMPLATE(
SUBPARTITION east VALUES('NY', 'VA', 'FL') TABLESPACE ts1,
SUBPARTITION west VALUES('CA', 'OR', 'HI') TABLESPACE ts2,
SUBPARTITION central VALUES('IL', 'TX', 'MO') TABLESPACE ts3)
( PARTITION janfeb_2000 VALUES LESS THAN (TO_DATE('1-MAR-2000','DD-MON-YYYY')), PARTITION marapr_2000 VALUES LESS THAN (TO_DATE('1-MAY-2000','DD-MON-YYYY')), PARTITION mayjun_2000 VALUES LESS THAN (TO_DATE('1-JUL-2000','DD-MON-YYYY')) );
When you use a template, Oracle names the subpartitions by concatenating the partition name, an underscore, and the subpartition name from the template.

Data disctionary views to view partitions:


DBA_TAB_PARTITIONS
DBA_PART_TABLES
DBA_PART_KEY_COLUMNS  - can find the partitioned key columns.

DBA_TAB_SUBPARTITIONS
DBA_SUBPART_KEY_COLUMNS
DBA_SUBPARTITION_TEMPLATES


select partition_name, substr(PARTITION_POSITION,1,3), PARTITION_NAME, TABLESPACE_NAME, HIGH_VALUE from dba_tab_partitions  
where table_name = '&tname';


10g  New features:
--------------------------

1)  Maximum number partitions allowed for an object has been increased from 64K-1 to 1024K-1.
2) Enhanced partition pruning for complex queries.
3) Resource optimized for DROP TABLE for partitioned tables, This feature enables drop large partitioned table is internally split to drop chunks of partitions.It will drop in incremental fashion which helps optimize resource consumption.


11g  New features:
--------------------------

1) Ref partitioning: 
The 11g REF partitioning allows you to partition a table based on the values of columns within other tables.
To create the reference-partitioned table you must specify "PARTITION BY REFERENCE" cluase in the create table statement. This cluase specified the referentila constraint name.
The referential constraint must be enabled and enforced.
Foreign key column will become the partitioned key value for that table.

Ex:

CREATE TABLE orders
    ( order_id           NUMBER(12),
      order_date         TIMESTAMP WITH LOCAL TIME ZONE,
      order_mode         VARCHAR2(8),
      customer_id        NUMBER(6),
      order_status       NUMBER(2),
      order_total        NUMBER(8,2),
      sales_rep_id       NUMBER(6),
      promotion_id       NUMBER(6),
      CONSTRAINT orders_pk PRIMARY KEY(order_id))
  PARTITION BY RANGE(order_date)
    ( PARTITION Q1_2005 VALUES LESS THAN (TO_DATE('01-APR-2005','DD-MON-YYYY')),
      PARTITION Q2_2005 VALUES LESS THAN (TO_DATE('01-JUL-2005','DD-MON-YYYY')),
      PARTITION Q3_2005 VALUES LESS THAN (TO_DATE('01-OCT-2005','DD-MON-YYYY')),
      PARTITION Q4_2005 VALUES LESS THAN (TO_DATE('01-JAN-2006','DD-MON-YYYY'))
    );

CREATE TABLE order_items
    ( order_id           NUMBER(12) NOT NULL,
      line_item_id       NUMBER(3)  NOT NULL,
      product_id         NUMBER(6)  NOT NULL,
      unit_price         NUMBER(8,2),
      quantity           NUMBER(8),
      CONSTRAINT order_items_fk
      FOREIGN KEY(order_id) REFERENCES orders(order_id)
    )
   PARTITION BY REFERENCE(order_items_fk);


2) Interval partitions: 
This is a new 11g partitioning scheme that automatically creates time-based partitions as new data is added.
If you are using Range partitions there is maintanance cost. 
Note that the partition names are system generated,but you can rename them if you like.

Ex:
create table selling_stuff_daily
( 
prod_id number not null, cust_id number not null
, sale_dt date not null, 
qty_sold number(3) not null
, unit_sale_pr number(10,2) not null
, 
total_sale_pr number(10,2) not null
, total_disc number(10,2) not 
null)
partition by range (sale_dt)
interval 
(numtoyminterval(1,'MONTH'))
( partition p_before_1_jan_2007 
values
less than (to_date('01-01-2007','dd-mm-yyyy')));

For day interval partition:
numtodsinterval(1,'DAY')

In this case, Oracle will create the next partition for dates less than 02-01-2007 when the first record that 
belongs in that partition is created. Other than the interval keyword, this statement is just like a regular create table statement with range partition.



Friday, February 14, 2014

TNS-12557: TNS:protocol adapter not loadable & TNS-12555 TNS:permission denied

unable start/stop/status the listener in 10g database, was getting below error.

No longer listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=host_name)(PORT=1521)))
TNS-12557: TNS:protocol adapter not loadable
 TNS-12560: TNS:protocol adapter error
  TNS-00527: Protocol Adapter not loadable



 ls -ld /var/tmp
drwxrwx--T 11 root root 4096 Feb 13 23:07 /var/tmp


Reason: Permission issue on /var/tmp/.oracle directory.
               Can be an older installation of Oracle with different ownership of oracle OS user, Where oracle will create some socket files.

Solution: 
             Change the permissions for /var/tmp/.oracle directory to 01777 as root. Still if you see the same error remove the .oracle ditrectory from /var/tmp.

We could not find any info in the listener.log file then we enabled the trace for listener and found that it is permission issue on directory f/s.

vi listener.ora
TRACE_LEVEL_LISTENER= 16 (OFF default)


 ls -ld /var/tmp
drwxrwx--T 11 root root 4096 Feb 13 23:07 /var/tmp

changed permissions to 01777

ls -ld /var/tmp/.oracle
drwxrwxrwt 2 root root 4096 Sep 23 23:08 /var/tmp/.oracle

still same issue..

26> lsnrctl start

LSNRCTL for Linux: Version 10.2.0.4.0 - Production on 14-FEB-2014 02:30:32

Copyright (c) 1991, 2007, Oracle.  All rights reserved.

Starting /opt/oracle/product/10.2.0.4/bin/tnslsnr: please wait...

TNSLSNR for Linux: Version 10.2.0.4.0 - Production
System parameter file is /var/opt/oracle/listener.ora
Log messages written to /opt/oracle/admin/sql_net/listener.log
Trace information written to /opt/oracle/admin/sql_net/listener.trc
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=host_name)(PORT=1521)))
Error listening on: (ADDRESS=(PROTOCOL=ipc)(KEY=PNPKEY))
TNS-12555: TNS:permission denied
 TNS-12560: TNS:protocol adapter error
  TNS-00525: Insufficient privilege for operation
   Linux Error: 1: Operation not permitted


ls -lrrt .oracle*
total 0
srwxrwx--- 1 root root 0 Jul 14  2010 s#11020.2
srwxrwx--- 1 root root 0 Jul 14  2010 s#11020.1
srwxrwx--- 1 root root 0 Sep 23 23:08 sPNPKEY
srwxrwx--- 1 root root 0 Sep 23 23:08 s#8696.2
srwxrwx--- 1 root root 0 Sep 23 23:08 s#8696.1

Then as root removed the existing .oracle directory and recreated same structure as owner root:dba 


ls -rlt .oracle*
total 0
srwxr-x--- 1 oracle dba 0 Feb 14 02:43 sPNPKEY
srwxr-x--- 1 oracle dba 0 Feb 14 02:43 s#8696.2
srwxr-x--- 1 oracle dba 0 Feb 14 02:43 s#8696.1
srwxr-x--- 1 oracle dba 0 Feb 14 02:43 s#11020.2
srwxr-x--- 1 oracle dba 0 Feb 14 02:43 s#11020.1


lsnrctl start

LSNRCTL for Linux: Version 10.2.0.4.0 - Production on 14-FEB-2014 02:50:23

Copyright (c) 1991, 2007, Oracle.  All rights reserved.

Starting /opt/oracle/product/10.2.0.4/bin/tnslsnr: please wait...

.. ..
.....
 ...

The listener supports no services
The command completed successfully


ps -ef|grep tns
oracle    1881  9043  0 04:28 pts/2    00:00:00 grep tns
oracle    8391     1  0 02:53 ?        00:00:00 /opt/oracle/product/10.2.0.4/bin/tnslsnr LISTENER -inherit






Thursday, January 30, 2014

When to rebuild the indexes

Knowing when to Rebuild Indexes:


There are two rules of thumb to help determine if the index needs to be rebuilt.

1)     If the index has height greater than four, rebuild the index.

2)     The deleted leaf rows (index nodes) should be less than 20%.


Index height


The height of the index refers to the number of levels that are spawned by the index as a result in row inserts. When a large amount of rows are added to a table, Oracle may spawn additional levels of an index to accommodate the new rows. Hence, an Oracle index may have four levels, but only in those areas of the index tree where the massive inserts have occurred. Oracle indexes can support many millions of entries in three levels, and any SAP index that has four or more levels would benefit from rebuilding.


The number of deleted leaf nodes

The term "deleted leaf node" refers to the number of index nodes that have been logically deleted as a result of row deletes. Remember that Oracle leaves "dead" index nodes in the index when rows are deleted. This is done to speed up SQL deletes, since Oracle does not have to rebalance the index tree when rows are deleted.

We must first get an idea of the current state of the index by using the ANALYZE INDEX VALIDATE STRUCTURE command.
The VALIDATE STRUCTURE command can be safely executed without affecting the optimizer. The VALIDATE STRUCTURE command populates the SYS.INDEX_STATS table only. The SYS.INDEX_STATS table can be accessed with the public synonym INDEX_STATS. The INDEX_STATS table will only hold validation information for one index at a time. You will need to query this table before validating the structure of the next index.
Below is a sample output from INDEX_STATS Table.

SQL> ANALYZE INDEX IDX_GAM_ACCT VALIDATE STRUCTURE;
 
Statement processed.
 
SQL> SELECT name, height,lf_rows,lf_blks,del_lf_rows FROM
INDEX_STATS;
NAME                      HEIGHT    LF_ROWS    LF_BLKS    DEL_LF_ROW
---------------------- -----------   ----------      ----------   ----------------
DX_GAM_ACCT           2             1                     3               6
 
1 row selected.
 

SQL> ANALYZE INDEX IDX_GAM_FID VALIDATE STRUCTURE;
 
Statement processed.
 
SQL> SELECT name, height, lf_rows, del_lf_rows, (del_lf_rows/lf_rows)
*100 as ratio FROM INDEX_STATS;
 
NAME                           HEIGHT     LF_ROWS    DEL_LF_ROW RATIO    
------------------------------ ---------- ---------- ---------- -------
IDX_GAM_FID                                  1          189         62        32.80
 
1 row selected.
 
In this example, the ratio of deleted leaf rows to total leaf rows
is clearly above 20%. This is a good candidate for rebuilding.
Let’s rebuild the index and examine the results
 
SQL> ANALYZE INDEX IDX_GAM_FID REBUILD;
 
Statement processed.
 
SQL> ANALYZE INDEX IDX_GAM_FID VALIDATE STRUCTURE;
 
Statement processed.
 
SQL> SELECT name, height, lf_rows, del_lf_rows, (del_lf_rows/lf_rows)*
100 as ratio FROM INDEX_STATS;
 
NAME                           HEIGHT     LF_ROWS    DEL_LF_ROW RATIO    
------------------------------ ---------- ---------- ---------- -------
IDX_GAM_FID                                  1          127         0        0
 
1 row selected.

Examining the INDEX_STATS table shows that the 62 deleted leaf rows were 
dropped from the index. Notice that the total number of leaf rows went from 189 
to 127, which is a difference of 62 leaf rows (189-127). This index should 
provide better performance for the application.


REBUILD INDEXES:


Using ALTER INDEX REBUILD to Rebuild Indexes.

The ALTER INDEX index_name REBUILD command is very safe way to rebuild indexes. Here is the syntax of the command:

Unlike the traditional method where we drop the index and recreate it, the REBUILD command does not require a full table scan of the table, and the subsequent sorting of the keys and rowids. Rather, the REBUILD command will perform the following steps:


  1. Walk the existing index to get the index keys.
  2. Populate temporary segments with the new tree structure.
  3. Once the operation has completed successfully, drop the old tree, and rename the temporary segments to the new index.



RAC: CRS-4640: Oracle High Availability Services is already active


Error:
/opt/grid/11.2.0.3/bin/crsctl start crs
CRS-4640: Oracle High Availability Services is already active
CRS-4000: Command Start failed, or completed with errors.
-bash-3.2#


Reason:
sometimes when CRS server reboot it try to create sockets under /tmp/.oracle or /var/tmp/.oracle and there are already previous socket files...which are protecting to create new sockets.

Solution:
login with root user and remove all files under /tmp/.oracle or /var/tmp/.oracle restart CRS on faulty node or even reboot that machine!, start the crs and it will come up 

cursor delete with commit size



declare
  i number := 0;

 cursor s1
 is

 select seq_num from Tab where <>;


begin
  for c1 in s1 loop

       delete from TAb t1 where t1.seq_num = c1.seq_num;

           i := i + 1;              -- Commit after every X records

      if i > 10000 then
        commit;
        i := 0;
      end if;
  end loop;
      commit;
end;

SQL>

Tuesday, January 28, 2014

exclude in impdp

The below example gives syntax to EXCLUDE multiple tables and multiple schemas while doing a full database export using expdp

=== BEGIN expdp_exclude.par

DIRECTORY=DATA_PUMP_DIR
DUMPFILE=abc.dmp
LOGFILE=abc.log
FULL=Y
EXCLUDE=STATISTICS
EXCLUDE=TABLE:"IN ('NAME', 'ADDRESS' , 'EMPLOYEE' , 'DEPT')"
EXCLUDE=SCHEMA:"IN ('WMSYS', 'OUTLN')"

=== END expdp_exclude.par

In the above example parameter file; tables NAME and ADDRESS are owned by SCOTT and tables EMPLOYEE and DEPT are owned by HR
EXCLUDE=TABLE => You do not have to prefix the OWNER name, in fact, if you put the OWNER.TABLE_NAME, it would not work.
It will EXCLUDE all TABLES having the name mentioned in the list, even if more than one owner has the same object name.
For example: If ADDRESS table is owned by user SCOTT and user HR, that table will be EXCLUDED from both the users.

The above commands would work only via parameter file and would not work on the command line.


COMMAND LINE SYNTAX for EXPDP

expdp system/password DIRECTORY=DATA_PUMP_DIR DUMPFILE=abc.dmp FULL=Y
EXCLUDE=TABLE:\"IN \(\'NAME\', \'ADDRESS\' , \'EMPLOYEE\' , \'DEPT\'\)\"
EXCLUDE=SCHEMA:\"IN \(\'WMSYS\', \'OUTLN\'\)\"

Monday, January 13, 2014

Configure SSH between cluster nodes in Linux


Setting up SSH between the cluster nodes:


Setting up SSH on UNIX and Linux systems involves verifying that the SSH server daemon sshd is running and, if necessary, starting this daemon. Set up SSH on the DAS host and on all hosts where instances in your cluster will reside.
On UNIX and Linux systems, SSH software is typically installed as part of the base operating system. If SSH is not installed, download and install the appropriate OpenSSH SSH package for your operating system.


Each Node should be able to ssh to itself and each other.
If .ssh directory does not exist on the hosts create it.

cd ~  ( Make sure Oracle User Home is set to 750 – chmod 750 ~ )  (Very IMP..)
chmod -R og-rx .ssh ( or chmod 700)

On Node 1
1. cd ~/.ssh

2. Generate Public & Private RSA encryption Key pair
/usr/bin/ssh-keygen -t rsa
Enter file in which to save the key: /users/apps/dba/oracle/.ssh/id_rsa
Enter passphrase: No passphrase

3. Generate Public & Private DSA encryption Key pair
/usr/bin/ssh-keygen -t dsa
Enter file in which to save the key: /users/apps/dba/oracle/.ssh/id_dsa
Enter passphrase: No passphrase

4. cat ~ /.ssh/*.pub >> authorized_keys

5. scp the  authorized_keys to the subsequent Node under /users/apps/dba/oracle/.ssh

Repeat Step 1-5 on all Nodes. Once Step 4 is completed on the Last Node copy the authorized_keys from the Last Node to all other Nodes. This will replace the existing authorized_keys on the hosts with keys from the Last Node in the cluster.
Verify SSH

Make sure you have below soft-links for ssh/scp before you start the OUI Installer.
/usr/bin/ssh -> /opt/boksm/bin/ssh
/usr/bin/scp -> /opt/boksm/bin/scp
/usr/local/bin/ssh -> /opt/boksm/bin/scp
/usr/local/bin/scp -> /opt/boksm/bin/scp

Ssh as the oracle from each host in the cluster as below. If a password is prompted then ssh setup is done incorrectly.

ssh to both short primary host name and full primary host name.

From Host1: ssh Host1 date
From Host1: ssh Host2 date
From Host 2: ssh Host2 date
From Host 2: ssh Host1 date

Verify the above for all remaining hosts in the cluster.