HP NZ Database Services

2009/09/13

Beyond DB Time

This post follows on from a previous one in which I used a Statspack report to show the value of the time model statistics. In this post I use some guess work to look outside the DB Time metric, to get a feel for what else might affect the response time for the application, and to gauge how important tuning the database (reducing DB Time) is from a holistic point of view.

I should point out that I’m not as familiar with AIX (the OS in this case) as I am with Solaris, Linux, HP-UX or even Windows.  I had no access to the server in question during the period for the Statspack report.  These are theories.  Make up your own mind and send us feedback (see About us page for email address) if you have knowledge that you’d like to share.

The system was described to me as “CPU constrained”.  The Statspack report shows that the database was directly responsible for only 24% of the 78% CPU utilisation (in other words increased server CPU utilisation by 19%).

Host CPU  (CPUs: 2  Cores: 1  Sockets: 0)
~~~~~~~~              Load Average
                      Begin     End      User  System    Idle     WIO     WCPU
                    ------- -------   ------- ------- ------- ------- --------
                       5.90    5.92     44.49   34.03   21.47    1.47  309.01

Instance CPU
~~~~~~~~~~~~                                       % Time (seconds)
                                            -------- --------------
                     Host: Total time (s):                  7,199.4
                  Host: Busy CPU time (s):                  5,653.5
                   % of time Host is Busy:      78.5
             Instance: Total CPU time (s):                  1,364.2
          % of Busy CPU used for Instance:      24.1
        Instance: Total Database time (s):                  4,059.1
  %DB time waiting for CPU (Resource Mgr):       0.0

OS Statistics
-> ordered by statistic type (CPU use, Virtual Memory, Hardware Config), Name

Statistic                                  Total
------------------------- ----------------------
BUSY_TIME                                565,349
IDLE_TIME                                154,590
IOWAIT_TIME                               10,579
SYS_TIME                                 245,021
USER_TIME                                320,328
OS_CPU_WAIT_TIME                       2,224,700
RSRC_MGR_CPU_WAIT_TIME                         0
PHYSICAL_MEMORY_BYTES              8,589,934,592
NUM_CPUS                                       2
NUM_CPU_CORES                                  1
GLOBAL_RECEIVE_SIZE_MAX                1,048,576
GLOBAL_SEND_SIZE_MAX                   1,048,576
TCP_RECEIVE_SIZE_DEFAULT                  16,384
TCP_RECEIVE_SIZE_MAX      ######################
TCP_RECEIVE_SIZE_MIN                       4,096
TCP_SEND_SIZE_DEFAULT                     16,384
TCP_SEND_SIZE_MAX         ######################
TCP_SEND_SIZE_MIN                          4,096
          -------------------------------------------------------------

OS Statistics - detail 

  Snap Snapshot
    Id Day Time          Load  %Busy  %User %System   %WIO  %WCPU
------ --------------- ------ ------ ------ ------- ------ ------
  1654 Tue 18 11:00:04    5.9
  1655 Tue 18 11:31:02    7.5   82.5   46.5    36.0    1.9
  1656 Tue 18 12:00:04    5.9   74.3   42.4    31.9    1.0
          -------------------------------------------------------------

This server is dedicated to the one database, and has no application component running on it.  So what was using the rest of the CPU?  Perhaps the database CPU measurements were not quite correct, (eg due to LOB OCI activity?), or perhaps the listener was using some CPU (tracing enabled?), but it seems to me there is more to it than that.  I see that 44% of the CPU busy time happened in system mode.  This could be related to I/O or network traffic (among other things).  It was subsequently discovered that the I/O was being cached by the OS, so perhaps that contributed to some degree.

Finding the cause might offer some opportunity for tuning, greater than just trying to reduce background and DB CPU time.

How about network traffic?

I already know that this application uses LOBS, (direct I/O to LOBs showed up in the Statspack report), and  LOB access can result in a high number of round trips.

                                                              Avg          %Total
                                          %Tim Total Wait   wait    Waits   Call
Event                               Waits  out   Time (s)   (ms)     /txn   Time
---------------------------- ------------ ---- ---------- ------ -------- ------
SQL*Net message from client    16,327,445    0    471,448     29     69.9

Statistic                                      Total     per Second    per Trans
--------------------------------- ------------------ -------------- ------------
bytes received via SQL*Net from c      5,432,266,525    1,508,962.9     23,262.5
bytes sent via SQL*Net to client       9,743,180,327    2,706,439.0     41,723.1
SQL*Net roundtrips to/from client         16,329,496        4,536.0         69.9

There were 16 million round trips and about 15GB of data transferred across the network during the hour.

4,536 round trips per second means 9,072 SQL*Net packets (up to 8000bytes) arriving or departing per second, which may then be broken into multiple network packets (up to 1500bytes), each potentially causing CPU interrupts which have to be handled by the OS. (“Potentially” because of interrupt coalescing).  Surely this would contribute to the CPU time system in system mode.

Further, it might show that the active database time is less significant to overall application performance than we might otherwise assume.

The total wait time was 471,448 seconds, most of which will be idle application time, but not all of it.  Some of this time is network overhead, and some will be application think time.  It is not possible to calculate the network + application time from the data in the Statspack report, but we can make a safe guess at a lower limit using the wait event histogram information.

Total ----------------- % of Waits ------------------
Event                      Waits  <1ms  <2ms  <4ms  <8ms <16ms <32ms  <=1s   >1s
-------------------------- ----- ----- ----- ----- ----- ----- ----- ----- -----
SQL*Net more data to clien  203K  99.1    .2    .3    .3    .0    .0    .0
SQL*Net message from clien   16M  16.1  29.5  25.4  19.3   6.5   1.6   1.4    .2
SQL*Net message to client    16M 100.0    .0    .0    .0    .0    .0    .0
SQL*Net more data from cli   60K  58.5    .2    .2    .2   2.3  38.1    .4

If we decide to treat any SQL*Net wait event longer than one second as idle (application and DB doing nothing), and treat the rest as time during which the user would be waiting, then we can get a lower limit from the highlighted row above.  (I’ll ignore the less significant more data waits for this example).

0ms <= 16.1% of waits < 1ms     0.161 * 0ms * 16,327,445 = 0s
1ms <= 29.5% of waits < 2ms     0.295 * 1ms * 16,327,445 = 4,816s
etc...

Taking the lower bound of each I calculate at that 45,701 sec is the lower limit.  Compare this to the total DB Time of 3,393 sec, and we can see that the time spent in the database is no more than 6.9% of the total response time of the app server + database server components.

Note that this doesn’t include any time used for communication between the application server and the user, browser processing, etc.

This extra information puts into perspective the scope of any benefit from reducing DB Time.  This information could be useful to the overall tuning effort by moving the focus to options that might help to reduce the number of round trips or make them more efficient.  (Eg bulk processing, pre-fetching, using varchar2 instead of LOB, increasing SDU, removing firewalls between app and DB, tuning interrupt coalescing parameters, application server tuning, etc).

2009/07/21

LOB vs Network

In a previous post,  I pointed out that SQL*Net message from client is not always an idle wait from the user’s point of view.

During a performance investigation, I traced the application that wasn’t performing to expectations.  Here is an excerpt from a trace file:

OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse     1145      0.12       0.13          0          0          0           0
Execute   1145      0.28       0.33          2          7         37           4
Fetch     1645      0.46       0.52          0       4314          0        5242
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     3935      0.86       0.99          2       4321         37        5246

Misses in library cache during parse: 0

Elapsed times include waiting on following events:
 Event waited on                             Times   Max. Wait  Total Waited
 ----------------------------------------   Waited  ----------  ------------
 SQL*Net message to client                    5197        0.00          0.02
 SQL*Net message from client                  5197        0.90         58.88
 SQL*Net more data to client                   516        0.00          0.05
 SQL*Net more data from client                  40        0.00          0.00
 direct path read                               28        0.00          0.02
 db file sequential read                         2        0.01          0.02
 log file sync                                   4        0.00          0.01

Most of the response time from the application/user’s point of view ocurred “outside” of the database.  What was surprising, was that so many round trips were required for what seems like a simple function of the application, and that the tkprof report doesn’t show the waits associated with a cursor.

Looking at the trace file, the SQL*Net round trips always followed certain SQL statements:

PARSING IN CURSOR #1 len=120 dep=0 uid=53 oct=3 lid=53 tim=19783142399235 hv=1095791004 ad='906c6620'
SELECT * FROM TABLE_A WHERE COL1=:a AND COL2=:b
END OF STMT
PARSE #1:c=0,e=195,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=19783142399223
EXEC #1:c=0,e=271,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=19783142399755
WAIT #1: nam='SQL*Net message to client' ela= 4 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142399974
WAIT #1: nam='SQL*Net more data to client' ela= 93 driver id=1952673792 #bytes=2001 p3=0 obj#=72685 tim=19783142400471
FETCH #1:c=0,e=374,p=0,cr=3,cu=0,mis=0,r=1,dep=0,og=1,tim=19783142400540
WAIT #1: nam='SQL*Net message from client' ela= 13126 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142413820
WAIT #0: nam='SQL*Net message to client' ela= 2 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142413956
WAIT #0: nam='SQL*Net message from client' ela= 8580 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142422616
WAIT #0: nam='SQL*Net message to client' ela= 3 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142422836
WAIT #0: nam='SQL*Net message from client' ela= 12421 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142435416
WAIT #0: nam='SQL*Net message to client' ela= 2 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142435546
WAIT #0: nam='SQL*Net message from client' ela= 10679 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142446296
WAIT #0: nam='SQL*Net message to client' ela= 2 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142446409
WAIT #0: nam='SQL*Net message from client' ela= 10579 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142457049
WAIT #0: nam='SQL*Net message to client' ela= 2 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142457144
WAIT #0: nam='SQL*Net message from client' ela= 11231 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142468451
WAIT #0: nam='SQL*Net message to client' ela= 1 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142468561
WAIT #0: nam='SQL*Net message from client' ela= 12782 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142481436
WAIT #0: nam='SQL*Net message to client' ela= 9 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142481763
WAIT #0: nam='SQL*Net message from client' ela= 7636 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142489618
WAIT #0: nam='SQL*Net message to client' ela= 3 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142489736
WAIT #0: nam='SQL*Net message from client' ela= 12990 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142502814
WAIT #0: nam='SQL*Net message to client' ela= 6 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142503127
WAIT #0: nam='SQL*Net message from client' ela= 10648 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142513875
WAIT #0: nam='SQL*Net message to client' ela= 2 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142514010
WAIT #0: nam='SQL*Net message from client' ela= 8445 driver id=1952673792 #bytes=1 p3=0 obj#=72685 tim=19783142522541

Notice the waits for cursor #0.  I guessed this was some sort of OCI call that happened after the rows for the cursor had been returned.  This seems to get used for low level work that isn’t associated directly to a user’s cursor.  When I examined TABLE_A I saw that it had several CLOBs and BLOBs in it.  Were they to blame for the high number of round trips?

I found some Metalink notes which explain what is going on:
Note:398380.1     Poor Performance When Using CLOBS and Oracle Net
Bug No. 3892362   CLOBS HAVE POOR NETWORK PERFORMANCE – REQUEST FOR PRODUCT ENHANCEMENT
Note:66431.1      LOBS – Storage, Redo and Performance Issues
Note:401589.1     Performance Problems Using Clob and Blobs
Bug:7341386

Basically, not only does having LOB columns in a select restrict each fetch to one row at a time, additional round trips are needed to handle LOB locators and to retrieve data from the LOBs.  This can cause performance problems with high latency networks because of the high number of round trips (over 5000 in our case).

However, in another Oracle note, it is said:

“If I write OCI code then I can define an array of OCILobLocators and use array fetch with this and you get multiple rows in each net packet. In summary I do not see any server bug here. Whether array fetch is used for CLOBs or not is up to the client code. “

From the manual http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28395/oci07lob.htm :

Array Interface for LOBs
You can use the OCI array interface with LOBs, just as with any other datatype. There are two ways of using the array interface.
1.    Using the data interface

You can bind or define arrays of character data for a CLOB column or RAW data for a BLOB column. You can use array bind and define interfaces to insert and select multiple rows with LOBs in one round trip to the server…..”

In the example traced above, the problem ended up being with the configuration of the virtual application server, (okay, so not strictly LOB vs Network this time), but any communication latency can be multiplied by applications that access LOBs via many round trips as in the example above.  This is another LOB related performance issue that can be difficult to identify.  (See the first one I posted about).

2009/06/20

Proving the Performance Problem is Outside of the Database

I have often been asked to investigate a performance problem, only to find to my disappointment that the main component of response time is outside of the Oracle database.  (This robs me of a challenging tuning exercise, and may limit how useful I can be to the customer).  The problem still needs to be identified and fixed, but this will only happen if the appropriate area is examined, so the DBA may have to convince the relevant technical staff to have closer look.

The “idle” wait event SQL*Net message from client is often ignored, but it is relevant during the period when the user is waiting for the application to respond.  It can be used to show that the issue is not one of database or SQL tuning, but elsewhere, eg networking devices or application server performance.  (Admittedly, the DBA may still be able to help in some cases, eg tuning the connection method, SQL*Net configuration and pre-fetch settings).

It doesn’t take long to trace a user’s session (with wait events), isolate the period during which the user is waiting for the application to respond, and then use tkprof to generate a report showing how much time was due to the database working and how much could be attributed to network/application server (SQL*Net message from client).

Sometimes this is enough for the DBA to stop investigating, but other times the people responsible for the network or application server need more evidence.

Monitoring the load (eg CPU) on the application server isn’t always going to be conclusive, especially when there are lots of round trips between the application server and the database.

In some cases the application server has been found to be the main cause of the delay, eg JVM garbage collections halting processing.  In the example I use below, it was proved to be a network problem, despite initial claims that it was not.

A useful next step in the situation where the Oracle database has been ruled out as a cause of the slow performance, is to narrow it down further by measuring the network packet arrivals and departures from the perspectives of the application server and the database server.  For one particular case, I used snoop and then fed the data into a free program called WireShark to analyse the results.

The snoop measurements from the Oracle database server side showed fast responses to incoming packets, which matched the SQL trace data.
The snoop measurements from the application server side also showed fast responses to incoming packets.

These results indicated that the ‘network’, (TCP stack, NIC, cable, switch, port, etc), was the cause of most of the delay.  I just needed a little more lower level evidence, so I tried pings from both servers, which showed RTTs of >10ms.  The servers were on different subnets, so there had to be at least one switch or router in between.  We needed more granularity.
A traceroute from each server showed this:

[dbserver:root]# traceroute appserver
 traceroute to appserver (nn.nn.40.49), 30 hops max, 40 byte packets
 1  nn.nn.38.2 (nn.nn.38.2)  0.521 ms  0.396 ms  0.322 ms
 2  appserver (nn.nn.40.49)  11.968 ms  8.708 ms  13.582 ms
[appserver:root]# traceroute dbserver
 traceroute to dbserver (nn.nn.38.166), 30 hops max, 40 byte packets
 1  nn.nn.40.2 (nn.nn.40.2)  1.149 ms  0.997 ms  0.740 ms
 2  dbserver (nn.nn.38.166)  11.134 ms  12.107 ms  13.027 ms

Hop 1 is to a switch/router and was consistently fast, proving that the cause wasn't local to one of the servers.
Hop 2 was always slow, between the switch/router and the remote server, in both directions. It must be the switch causing the problem!

These results were repeatable, so we had enough reason to ask the network engineer to take another look.

The network engineer found that the switch/router suffered from a "known problem".   Something about other ports on the same line card running at slower speeds but using the same buffer.... I think that means that the output queue might have been slower when sending large amounts of data to a slower network.

Theme: Rubric. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.