One of the most confounding problems with Oracle is the resolution of buffer busy wait events. Buffer busy waits are common in an I/O-bound Oracle system, as evidenced by any system with read (sequential/scattered) waits in the top-five waits in the Oracle STATSPACK report, like this:
Top 5 Timed Events
Event Waits Time (s) Ela Time
--------------------------- ------------ ----------- -----------
db file sequential read 2,598 7,146 48.54
db file scattered read 25,519 3,246 22.04
library cache load lock 673 1,363 9.26
CPU time 2,154 934 7.83
log file parallel write 19,157 837 5.68
The main way to reduce buffer busy waits is to reduce the total I/O on the system. This can be done by tuning the SQL to access rows with fewer block reads (i.e., by adding indexes). Even if we have a huge db_cache_size, we may still see buffer busy waits, and increasing the buffer size won't help.
In order to look at systemwide wait events, we can query the v$system_event performance view. This view, shown in Listing A, provides the name of the wait event, the total number of waits and timeouts, the total time waited, and the average wait time per event.
The type of buffer that causes the wait can be queried using the v$waitstat view. This view lists the waits per buffer type for buffer busy waits, where COUNT is the sum of all waits for the class of block, and TIME is the sum of all wait times for that class:
select * from v$waitstat;
CLASS COUNT TIME
------------------ ---------- ----------
data block 1961113 1870278
segment header 34535 159082
undo header 233632 86239
undo block 1886 1706
Buffer busy waits occur when an Oracle session needs to access a block in the buffer cache, but cannot because the buffer copy of the data block is locked. This buffer busy wait condition can happen for either of the following reasons:
- The block is being read into the buffer by another session, so the waiting session must wait for the block read to complete.
- Another session has the buffer block locked in a mode that is incompatible with the waiting session's request.
Because buffer busy waits are due to contention between particular blocks, there's nothing you can do until you know which blocks are in conflict and why the conflicts are occurring. Tuning therefore involves identifying and eliminating the cause of the block contention.
The v$session_wait performance view, shown in Listing B, can give some insight into what is being waited for and why the wait is occurring.
The columns of the v$session_wait view that are of particular interest for a buffer busy wait event are:
- P1—The absolute file number for the data file involved in the wait.
- P2—The block number within the data file referenced in P1 that is being waited upon.
- P3—The reason code describing why the wait is occurring.
Here's an Oracle data dictionary query for these values:
p1 "File #".
p2 "Block #",
p3 "Reason Code"
event = 'buffer busy waits';
If the output from repeatedly running the above query shows that a block or range of blocks is experiencing waits, the following query should show the name and type of the segment:
file_id = &P1
&P2 between block_id and block_id + blocks -1;
Once the segment is identified, the v$segment_statistics performance view facilitates real-time monitoring of segment-level statistics. This enables a DBA to identify performance problems associated with individual tables or indexes, as shown in Listing C.
We can also query (Listing D) dba_data_files to determine the file_name for the file involved in the wait by using the P1 value from v$session_wait for the file_id.
Interrogating the P3 (reason code) value from v$session_wait for a buffer busy wait event will tell us why the session is waiting. The reason codes range from 0 to 300 and can be decoded, as shown in Table A.
As I mentioned at the beginning of this article, buffer busy waits are prevalent in I/O-bound systems. I/O contention, resulting in waits for data blocks, is often due to numerous sessions repeatedly reading the same blocks, as when many sessions scan the same index. In this scenario, session one scans the blocks in the buffer cache quickly, but then a block has to be read from disk. While session one awaits the disk read to complete, other sessions scanning the same index soon catch up to session one and want the same block currently being read from disk. This is where the buffer busy wait occurs—waiting for the buffer blocks that are being read from disk.
The following rules of thumb may be useful for resolving each of the noted contention situations:
- Data block contention—Identify and eliminate HOT blocks from the application via changing PCTFREE and or PCTUSED values to reduce the number of rows per data block. Check for repeatedly scanned indexes. Since each transaction updating a block requires a transaction entry, increase the INITRANS value.
- Freelist block contention—Increase the FREELISTS value. Also, when using Parallel Server, be certain that each instance has its own FREELIST GROUPs.
- Segment header contention—Again, increase the number of FREELISTs and use FREELIST GROUPs, which can make a difference even within a single instance.
- Undo header contention—Increase the number of rollback segments.
The identification and resolution of buffer busy waits can be very complex and confusing. Oracle provides the v$segment_statistics view to help monitor buffer busy waits, and the v$system_event views to identify the specific blocks for the buffer busy wait. While identifying and correcting the causes of buffer busy waits is not an intuitive process, the results of your efforts can be quite rewarding.