Wednesday, August 22, 2012

Changes...

Sometimes we get so used to the way things are that we forget things change, and normally such changes get us out of our comfort zone. Last Saturday I was working on another project, installing a 3 nodes RAC database, 11g release 2. Oracle has improved its installers, so normally they are able to do a good job checking and fixing grid and database pre-requisites, especially on Linux platforms. But this time I could not enjoy the convenience of a GUI as I was working remotely through (a slow) VPN. So before running a silent installation I set manually the SSH configuration between the nodes and ran the cluster verify utility:

 

 

node1:grid: ./runcluvfy.sh stage -pre crsinst –n node1,node2,node3 -fixup

 

WARNING:

Could not access or create trace file path "/tmp/bootstrap/cv/log". Trace information could not be collected

 

Performing pre-checks for cluster services setup

 

Checking node reachability...

node1.domain: node1.domain

 

Check: Node reachability from node "null"

  Destination Node                    Reachable?

  ------------------------------------  ------------------------

  node1                                        no

  node2                                        no

  node3                                        no

 

Result: Node reachability check failed from node "null"

 

ERROR:

Unable to reach any of the nodes

Verification cannot proceed

 

 

At first it seems a DNS problem, but:

 

node1:grid: /usr/bin/nslookup node1

Server:         10.xxx.x.254

Address:        10.xxx.x.254#53

 

Name:   node1

Address: 200.xxx.xxx.54

 

 

Ping between nodes worked…

 

node1:grid: ping 200.xxx.xxx.54

PING 200.xxx.xxx.54 (200.xxx.xxx.54) 56(84) bytes of data.

64 bytes from 200.xxx.xxx.54: icmp_seq=1 ttl=64 time=0.027 ms

64 bytes from 200.xxx.xxx.54: icmp_seq=2 ttl=64 time=0.026 ms

 

 

Google returned 2 or 3 posts, but all them related to wrong server/domain names or the server name not set correctly in the DNS.

 

Finally I found the problem: the file /etc/nsswitch.conf was wrong. DNS was not part of the Name Service resolution :o(

 

The file should have a line like this:

hosts:  dns files

 

However the "hosts:" service line was:

hosts:  files

And the server names were not defined in the /etc/hosts files.

 

The Installation Guides did not make any mention to nsswitch.conf file. I found it on this book: Oracle® Database 2 Day + Real Application Clusters Guide topic 8.

 

The funny thing is the file was wrong on just one node (that node was used to run some connectivity tests before installing Oracle). Well, documentation is my friend, and I got one more item for my checklist – things change: all the time ;o)

Saturday, July 28, 2012

Physical data distribution

Tuning application queries is one of the biggest challenges for DBAs supporting production environments. The lack of knowledge about the application data model makes difficult the tuning. I remember when I worked as a development DBA – based on my knowledge about the application design I was able to identify whether the access path was optimal or not just looking at explain plans. It was easy to identify how a particular query should run.

 

Without application knowledge, improvements may be obtained by considering the table sizes and the number of rows that can be eliminated as early as possible in the query based on the join and filter conditions. When tables are accessed in the expected order, rows are eliminated correctly generating no more I/O operations (physical or logical) than the necessary. It is pretty common to see problematic situations created by just few SQL statements with non-optimal execution plans generating so many and unnecessary physical I/O in such way that the overall performance for the database is degraded.

 

Normally tuning OLTP queries is less complicated than tuning queries running on data warehouse systems, as the queries use to be smaller and simpler. Last week I was working on a query that is used to show offers to customers contacting the call center. Most part of the time (99%) the query runs very fast, in less than 100 milliseconds. But some executions take minutes, raising the average execution time for the query to 20 seconds – I am mentioning the average time just to give a better idea how extremely opposite the execution times are.

 

The execution time is related to the number of rows accessed by the query – fast executions access few rows, and the slow executions access some thousands rows. The query joins 7 tables, but only 3 are really relevant: one because it is the most selective table (it is the driven table) and the other two because they hold most records accessed by the query, being the top contributors for the high execution times. This is the simplified query that I am going to use to show how unordered physical rows (based on join predicates) can impact the response time due to the high number of I/O operations:

 

SELECT /*+ ALL_ROWS */

       ... -- 1 indexed column from T1, 5 columns from T2, and 14 columns from T7

  FROM T1,

       T2,

       T7

 WHERE T1.UID = :B1

   AND T2.PER_ID = T1.ROW_ID

   AND T7.ID = T2.ROW_ID

   AND T7.FLAG = 'Y'

 

Predicate

Rows

 

access(T1.UID= '4850')

1

access(T2.PER_ID=T1.ROW_ID)

4,973

access(T7. ID=T2.ROW_ID)

4,893

filter(T7.FLAG='Y')

3

access(T3.ROW_ID= T7.ROW_ID)

3

 

The query was taking 41 seconds.

 

I/O Statistics

 

Consistent gets

19,281

Physical reads

7,585

 

The following table shows the number of blocks that contains any row accessed by the query. It shows the number of blocks before and after reorganizing the physical order of the rows in tables T2 and T7.

 

Table

Rows

Before (blocks)

After (blocks)

 

T2

4,973

4,426

75

T7

4,893

120

23

 

The reorganization was pretty simple, as I was working only with records related to a sample case instead of reorganizing the entire table:

 

§   Created a temporary table with the relevant records;

§   Deleted the records from the current table;

§   Inserted the records back using a select ordered by the predicate column (PER_ID for T2 and ID for T7).

 

After reorganizing the tables T2 and T7 the query was taking 4.05 seconds.

 

I/O Statistics

 

Consistent gets

11,357

Physical reads

3,133

 

Analyzing the path access it is possible to reduce a little bit more the number of I/O operations. Oracle is using an index on column ID to access table T7 when joining it with T2. The column FLAG used as a filter condition is not part of the index, so oracle needs to access the table in order to check if the FLAG value satisfies the filter condition or not (T7.FLAG='Y').  Adding the column FLAG to the index will allow oracle to check the condition without accessing the table. After adding the FLAG column to the index this is the elapsed time and statistics for the query:

 

Elapsed time: 2.44 seconds

 

I/O Statistics

 

Consistent gets

10,081

Physical reads

2,819

 

Keeping the physical order of rows based on a specific logical order can be a nightmare. When the application inserts data through batch or bulk loads it may be simple to keep the physical order of rows based on a logical order (index). But normally data is inserted at different period of times, and without any order, result of online processing. It should be necessary to schedule table reorganizations on a regular basis to keep the physical order close to the logical order – I support some databases with tables measured in hundreds of gigabytes, with active data (not historical), and accessed 24x7x365 – reorganization is not feasible.

 

Concerning other access paths (for example, T2 was reorganized by PER_ID, but it has several other indexes used to satisfy different queries), the reorganization approach might decrease the table access efficiency thru other indexes. In this specific case of table T2, where the data was spread over thousands of blocks, the reorganization concentrated the rows in just few hundreds, improving all access paths.

 

Besides, this is a RAC database (for sake of sanity I ran all tests on the same node, and yes, I used the Test environment :o), therefore by using the reorganization approach there is a good chance to increase the interconnect traffic, as the rows will be grouped into fewer (hot) blocks.

 

Bottom line is there is no magic - bad design or a design that does not reflect easily the business requirements will cost money to fix, either for reengineering the application, or for acquiring powerful hardware.