Oracle TimesTen 11.2.2.4.0 was released

The new TimesTen version is now available on OTN (http://www.oracle.com/technetwork/products/timesten/downloads/index.html).

Some of the new features:
  • This release contains an Index Adviser that can be used to recommend a set of indexes that can improve the performance of a specific workload. 
  • You can now load a TimesTen table with the result of a query executed on an Oracle database. This feature does not require you to create a cache group.
  • The TimesTen Cache Advisor provides recommendations on how to initially configure a cache schema, to identify porting issues and to estimate the performance improvement of a specific workload. The Cache Advisor is available on Linux x86-64.
Well, now TimesTen contains different advisers which should provide some recommendations about indexing and caching. Additionally, there is some opportunity to cache data by using sql query. I am very interested about these new features and will definitely write about it in details.

PS. What will be in TimesTen 12c vertion?

PLSQL Server Pages

Recently, I’ve received a very interesting task at my job, I have to create a colourful report which uses different colours depending on values (if value < 5 then cell should has a green colour, if value between 5 and 9 then yellow, else red).


There are a lot of methods to create a report like this. First approach based on using any BI tool ( like SAS, Oracle BI, BO and etc.). These tools allow you to create a report and upload all data into Excel file. There is only one disadvantage – they cost money, so in my case I can’t use it because I don’t have a budget for that.
Second approach based on uploading a CSV file from Oracle DB by using PL/SQL, but in this case there is no opportunity to control the cells’ colours, so I’ve decided using PL/SQL server pages feature.

Let’s create a  simple example. First of all, create test tablespace and objects owner user.
SQL> create tablespace gena_tbls datafile '/u01/app/oracle/oradata/orcl/gena.dbf' size 100m autoextend on;

Tablespace created.

SQL> create user gena identified by gena default tablespace gena_tbls;

User created.

SQL> grant connect, resource to gena;

Grant succeeded.
Ensure that the database account ANONYMOUS is unlocked.
SQL> alter user anonymous account unlock;

User altered.
Create a simple table.
SQL> connect gena/gena
Connected.

SQL> create table t1 ( indicator_name varchar2(50), value number);

Table created.

SQL> insert into t1 values ('INDIC 1', 1);

1 row created.

SQL> insert into t1 values ('INDIC 2', 6);

1 row created.

SQL> insert into t1 values ('INDIC 3', 10);

1 row created.

SQL> insert into t1 values ('INDIC 4', 4);

1 row created.

SQL> insert into t1 values ('INDIC 5', 8);

1 row created.

SQL> commit;

Commit complete.

SQL>
Log on to the database as an XML DB administrator (SYS in this case), that is a user with the XDBADMIN role and create the DAD.
SQL> connect / as sysdba
Connected.
SQL> exec dbms_epg.create_dad('gena_dad', '/gena_report/*');

PL/SQL procedure successfully completed.

SQL>

Set the DAD attribute database-username to the database user whose privileges must be used by the DAD.
SQL> exec dbms_epg.set_dad_attribute('gena_dad', 'database-username', 'gena');

PL/SQL procedure successfully completed.

SQL>
Grant EXECUTE privilege to the database user GENA whose privileges must be used by the DAD.
SQL> grant execute on dbms_epg to gena;

Grant succeeded.

SQL>
Log on to the database as the database user whose privileges must be used by the DAD and authorize the embedded PL/SQL gateway to invoke procedures and access document tables through the DAD.
SQL> connect gena/gena
Connected.
SQL> exec dbms_epg.authorize_dad('gena_dad');

PL/SQL procedure successfully completed.

SQL>
Create a sample PL/SQL stored procedure. This procedure creates an HTML page that includes the result set of a query of gena.t1.

Ensure that the listener is able to handle HTTP requests.
[oracle@db ~]$ lsnrctl status listener

LSNRCTL for Linux: Version 11.2.0.3.0 - Production on 31-JUL-2012 12:24:24

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

Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=db)(PORT=1521)))
STATUS of the LISTENER
------------------------
Alias                     listener
Version                   TNSLSNR for Linux: Version 11.2.0.3.0 - Production
Start Date                31-JUL-2012 12:24:00
Uptime                    0 days 0 hr. 0 min. 24 sec
Trace Level               off
Security                  ON: Local OS Authentication
SNMP                      OFF
Listener Parameter File   /u01/app/oracle/product/11.2.0/dbhome_1/network/admin/listener.ora
Listener Log File         /u01/app/oracle/diag/tnslsnr/db/listener/alert/log.xml
Listening Endpoints Summary...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=db)(PORT=1521)))
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=db)(PORT=8080))(Presentation=HTTP)(Session=RAW))
Services Summary...
Service "orcl" has 1 instance(s).
  Instance "orcl", status READY, has 1 handler(s) for this service...
Service "orclXDB" has 1 instance(s).
  Instance "orcl", status READY, has 1 handler(s) for this service...
The command completed successfully
[oracle@db ~]$
Run a web browser and put the following address: http://db:8080/gena_report/print_indicators

NVL bug in TimesTen 11.2.2.2.0

Recently, I've found a message about weard NVL function behaviour on TimesTen forum and decided to make a test (funny bug).
[oracle@nodett1 ~]$ ttversion
TimesTen Release 11.2.2.2.0 (32 bit Linux/x86) (tt1122:53392) 2011-12-23T09:21:34Z
  Instance admin: oracle
  Instance home directory: /u01/app/oracle/product/11.2.2/TimesTen/tt1122
  Group owner: oinstall
  Daemon home directory: /u01/app/oracle/product/11.2.2/TimesTen/tt1122/info
  PL/SQL enabled.
[oracle@nodett1 ~]$
Command> create table test ( id number, name_1 varchar2(20), name_2 varchar2(20));

Command> select nvl(name_1, 123) t1, nvl(name_2, 123) t2 from test;
0 rows found.

Command> insert into test values (1,'test',null);
1 row inserted.

Command> select * from test;
< 1, test, <null> >
1 row found.

Command> select nvl(name_1, 123) t1, nvl(name_2, 123) t2 from test;
 2922: Invalid number type value
0 rows found.
The command failed.
Command>

Command> select nvl(name_1, 123) from test;
 2922: Invalid number type value
0 rows found.
The command failed.

Command> select nvl(name_2, 123) from test;
< 123 >
1 row found.
Command>

Command> insert into test values (2,null,'test');
1 row inserted.

Command> select * from test;
< 1, test, <null> >
< 2, <null>, test >
2 rows found.
Command>

Command> select nvl(name_1, 123) t1, nvl(name_2, 123) t2 from test;
 2922: Invalid number type value
0 rows found.
The command failed.
Command>

Command> select nvl(name_1, 123) from test;
 2922: Invalid number type value
0 rows found.
The command failed.

Command> select nvl(name_2, 123) from test;
< 123 >
 2922: Invalid number type value
1 row found.
The command failed.
Command>
It looks like TimesTen tries to convert first expression into second’s expression data type. Especially I like the last statement :). Be careful with bugs.

SQL query and columnar compression

I've already written about Columnar Compression in TimesTen and now I would like to share some interesting point about it.
Environment:
[oracle@nodett1 sql]$ ttversion
TimesTen Release 11.2.2.2.0 (32 bit Linux/x86) (tt1122:53392) 2011-12-23T09:21:34Z
Instance admin: oracle
Instance home directory: /u01/app/oracle/product/11.2.2/TimesTen/tt1122
Group owner: oinstall 
Daemon home directory: /u01/app/oracle/product/11.2.2/TimesTen/tt1122/info
PL/SQL enabled.
[oracle@nodett1 sql]$
Let's create a compression table and insert 1M rows.
Command> create table tab_comp (id number not null,
       >                        val_1   varchar2(40),
       >                        val_2   varchar2(40))
       > compress (val_1 by dictionary maxvalues = 255) optimized for read;

Command> desc tab_comp;

Table GENA.TAB_COMP:
  Columns:
    ID                              NUMBER NOT NULL
    VAL_1                           VARCHAR2 (40) INLINE
    VAL_2                           VARCHAR2 (40) INLINE
  COMPRESS ( VAL_1 BY DICTIONARY MAXVALUES=255 ) OPTIMIZED FOR READ

1 table found.
(primary key columns are indicated with *)

Command> alltables;
  GENA.CD$_1086048_2
  GENA.TAB_COMP
  ...
26 tables found.
Command>

Command> desc CD$_1086048_2;

Table GENA.CD$_1086048_2:
  Columns:
   *VAL_1                           VARCHAR2 (40) INLINE
    ##CD_REFCNT                     TT_INTEGER NOT NULL

1 table found.
(primary key columns are indicated with *)
Command>
Command> begin
       >   for i in 1 .. 1000000 loop
       >     insert into tab_comp
       >          values (i,
       >                  '1234567890123456789012345678901234567890',
       >                  '1234567890123456789012345678901234567890');
       >   end loop;
       > end;
       > /

PL/SQL procedure successfully completed.

Command> select count(*) from tab_comp;
< 1000000 >
1 row found.
Command> select * from CD$_1086048_2;
< 1234567890123456789012345678901234567890, 1000000 >
1 row found.
Command>
As you can see, I've inserted the same value in VAL_1 column one million times. The CD$_1086048_2 system table contains only one row and TAB_COMP table contains only one 1 byte pointer in VAL_1 column for one row in CD$_1086048_2 table. After that I execute the following simple query (select count(val_1) from tab_comp;).
Command> autocommit 0;
Command> showplan 1;
Command> select count(val_1) from tab_comp;

Query Optimizer Plan:

  STEP:                1
  LEVEL:               2
  OPERATION:           TblLkSerialScan
  TBLNAME:             TAB_COMP
  IXNAME:              
  INDEXED CONDITION:   
  NOT INDEXED:         


  STEP:                2
  LEVEL:               1
  OPERATION:           OneGroupGroupBy
  TBLNAME:             
  IXNAME:              
  INDEXED CONDITION:   
  NOT INDEXED:         

< 1000000 >
1 row found.
Command>
Command> showplan 0;
Command> timing 1;
Command> select count(val_1) from tab_comp;
< 1000000 >
1 row found.
Execution time (SQLExecute + Fetch Loop) = 0.050841 seconds.

Command> select count(val_1) from tab_comp;
< 1000000 >
1 row found.
Execution time (SQLExecute + Fetch Loop) = 0.054260 seconds.

Command> select count(val_1) from tab_comp;
< 1000000 >
1 row found.
Execution time (SQLExecute + Fetch Loop) = 0.046867 seconds.

Command> select count(val_1) from tab_comp;
< 1000000 >
1 row found.
Execution time (SQLExecute + Fetch Loop) = 0.049661 seconds.
Command>
I was confused!!! TimesTen optimizer has chosen the full TAB_COMP table scan and of course it took long time (~0.04s). In TimesTen documentation I found these sentences ‘You can compress tables at the column level, which stores data more efficiently. This eliminates redundant storage of duplicate values within columns and improves the performance of SQL queries that perform full table scans.’ So I was expecting that optimizer would rewrite the query for something like select count('##CD_REFCNT') from CD$_1086048_2, especially taking into account that all information needed for optimizer could be found in CD$_1086048_2 table.
Command> select count('##CD_REFCNT') from CD$_1086048_2;
< 1 >
1 row found.
Execution time (SQLExecute + Fetch Loop) = 0.000058 seconds.
Command> select count('##CD_REFCNT') from CD$_1086048_2;
< 1 >
1 row found.
Execution time (SQLExecute + Fetch Loop) = 0.000081 seconds.
Unfortunately, my assumptions were not confirmed. I hope TimesTen development team will include this feature in next release.

Client-side load balancing in Oracle RAC 11GR2

In this post I would like to write about Client-side load balancing in Oracle RAC 11gR2. At the beginning of this post lets have a look at this feature in previous versions (before 11gr2).

We have a four nodes cluster. Using a simple TNS descriptor for connection:
RACDB =
  (DESCRIPTION =
   (ADDRESS = (PROTOCOL = TCP)(HOST = rac1-vip.us.oracle.com)(PORT = 1521))
   (ADDRESS = (PROTOCOL = TCP)(HOST = rac2-vip.us.oracle.com)(PORT = 1521))
   (ADDRESS = (PROTOCOL = TCP)(HOST = rac3-vip.us.oracle.com)(PORT = 1521))    
   (ADDRESS = (PROTOCOL = TCP)(HOST = rac4-vip.us.oracle.com)(PORT = 1521))    
  (CONNECT_DATA =
      (SERVICE_NAME = racdb)
    )
  )
In this case, client will connect to the first node (on rac1 local listener) (rac1-vip.us.oracle.com) only (if it's available of cource). For avoiding this situation we had to use LOAD_BALANCE parameter.
RACDB =
  (DESCRIPTION =
   (LOAD_BALANCE=ON)
   (ADDRESS = (PROTOCOL = TCP)(HOST = rac1-vip.us.oracle.com)(PORT = 1521))
   (ADDRESS = (PROTOCOL = TCP)(HOST = rac2-vip.us.oracle.com)(PORT = 1521))
   (ADDRESS = (PROTOCOL = TCP)(HOST = rac3-vip.us.oracle.com)(PORT = 1521))    
   (ADDRESS = (PROTOCOL = TCP)(HOST = rac4-vip.us.oracle.com)(PORT = 1521))    
  (CONNECT_DATA =
      (SERVICE_NAME = racdb)
    )
  )
Now, Oracle Net will choose one address from the list of addresses in a random sequence that will allow clients to balance the load on the all four listeners.
It was in previous 11gr2 versions. How does it work in new version? In 11gR2 SCAN (Single Client Access Name) was introduced. The SCAN feature is a new 'layer' between clients and local listeners in cluster, that allows you to change the your cluster configuration (i.e add/remove nodes) without making configuration changes in their clients. It means that now you can use only one name for connection to RAC instead of using a set of VIPs.
RACDB1 =
 (DESCRIPTION =
  (ADDRESS = (PROTOCOL = TCP)(HOST = rac-scan.cluster.us.oracle.com)(PORT = 1521))
    (CONNECT_DATA =
      (SERVICE_NAME = racdb)
    )
  )
The SCAN is configured during the installation of Oracle Grid Infrastructure. There are 2 options for defining the SCAN:
  1. Define the SCAN in your corporate DNS (Domain Name Service)
  2. Use the Grid Naming Service (GNS).
Let's have a look on each option.

In first option your network administrator has to create a single name in DNS server that resolves up to 3 IP addresses using a round-robin algorithm. Three IP addresses are recommended considering load balancing and high availability requirements regardless of the number of servers in the cluster (but using two addresses is also possible). The IP addresses must be on the same subnet as your public network in the cluster. For example:
rac-scan.cluster.us.oracle.com IN A 192.168.2.141
                               IN A 192.168.2.142
                               IN A 192.168.2.143
That means, if you connect to RAC using RACDB1 descriptor and corporate DNS you will balance the load on DNS level (using round-robin algorithm). Each time when you are resolving the rac-scan.cluster.us.oracle.com name DNS will send you different SCAN VIP.
You can also to install Grid Infrastructure without DNS. In this case, you would use a hosts-file entry to resolve the SCAN to one IP address. Since you are using only one (and only one) SCAN VIP there is no opportunity for load balancing on SCAN level, but you are able to use load balancing across nodes VIPs described above. Second option is using GNS. Using GNS assumes you have a DHCP service for delivering IP for SCAN VIPs (not only) and corporate DNS available on your public network. During the cluster configuration you are defining the deligated subdomain and GNS VIP for GNS. Before that you should set up the DNS for subdomain deligation.In this case, three IP addresses will be acquired from a DHCP service to create the SCAN and name resolution for the SCAN will be provided by the GNS.
One interesting point is that Oracle Client only resolves SCAN name and transforms it into following:
  (DESCRIPTION =
   (ADDRESS = (PROTOCOL = TCP)(HOST = SCAN-vip1)(PORT = 1521))
   (ADDRESS = (PROTOCOL = TCP)(HOST = SCAN-vip2)(PORT = 1521))
   (ADDRESS = (PROTOCOL = TCP)(HOST = SCAN-vip3)(PORT = 1521))        
  (CONNECT_DATA =
      (SERVICE_NAME = racdb)
    )
  )
,where SCAN-vipN - SCAN VIPs address. As yo can see, in this case, clients will connect to the first SCAN VIP only (SCAN-vip1 in the example). For avoiding this situation using LOAD_BALANCE parameter:
RACDB1 =
 (DESCRIPTION =
 (LOAD_BALANCE=ON)
  (ADDRESS = (PROTOCOL = TCP)(HOST = rac-scan.cluster.us.oracle.com)(PORT = 1521))
    (CONNECT_DATA =
      (SERVICE_NAME = racdb)
    )
  )

This problem was described in detail in Igor Melnikov's post

According to Oracle documentation, this parameter would have 'ON' default values only if the specified DESCRIPTION_LIST tag. Therefore, for load balancing in this case, you should set the LOAD_BALANCE parameter to 'ON' state, even if the SCAN is used.

Conclusion

In conclusion I would like to emphasize that described above client-side load balancing technology is transparent to the application and it does not require application modifications for all interfaces based on OCI (OCI, ODBC, JDBC, ADO DB, BDE).

Columnar compression in TimesTen

In this article I would like to write about Columnar Compression in TimesTen.

Oracle TimesTen 11.2.2 introduced several new features. One of them it’s Columnar Compression. Tables could be compressed at the column level, which stores data more efficiently.

The compression unit in Timesten is a compressed column group. A compressed column group consists of set of columns. You can include one or more columns in compressed column group; however, a column can be included in only one compressed column group. Each compressed column group is limited to a maximum of 16 columns.