| Lesson 9 | Contention in Shared Server |
| Objective | Use the V$DISPATCHER view to identify contention in Shared Server |
Identify Contention via the V$DISPATCHER View
Contention is what happens when more demands are placed on a resource than it can immediately handle, requests start queuing rather than being served right away. For dispatchers specifically, contention shows up as either a high busy rate on existing dispatcher processes, or a steadily increasing wait time in those dispatchers' own response queues, the same response queues covered back in Lesson 1. V$DISPATCHER and V$QUEUE, both already introduced in Lesson 8, are the two views that actually track these conditions. Both collect statistics from the moment the instance starts, so a value you see right now always reflects the entire uptime, not just the last few minutes, which matters for how you interpret anything they show you.
The practical stakes here are straightforward: contention is what a user experiences as a slow connection or a slow response, even though the SQL they are running might be perfectly efficient. Diagnosing it correctly, dispatcher versus shared server versus something outside Shared Server entirely, is what separates a five minute parameter change from hours spent tuning something that was never actually the bottleneck.
Reading V$DISPATCHER for Contention
V$DISPATCHER's relevant columns for this are
NAME,
NETWORK,
BUSY,
IDLE,
MESSAGES, and
BYTES, all confirmed in Lesson 8. Comparing busy time against idle time gives a straightforward load figure per protocol:
SELECT name,
(busy / (busy + idle)) * 100 AS busy_percentage
FROM v$dispatcher;
A dispatcher with a busy percentage climbing over time, especially relative to its siblings handling the same protocol, is a dispatcher worth investigating further. Because these figures are cumulative since startup, a single high reading on a newly started instance means much less than the same reading on one that has been running steadily for weeks; a dispatcher that spikes to 80 percent busy in its first hour and then levels off is a different situation than one that has been climbing toward 80 percent for a month straight.
MESSAGES and
BYTES round out the picture, though they answer a slightly different question than busy percentage does. Two dispatchers with similar busy percentages but very different message counts suggest very different workloads: one handling many small, quick requests, the other fewer but heavier ones. That distinction matters when deciding how to respond, since a dispatcher busy with many small requests scales differently with additional dispatchers than one busy with a smaller number of large ones.
Reading V$QUEUE for the Same Question
Queue depth and wait time live in
V$QUEUE, not
V$DISPATCHER, a distinction worth being precise about since the two views are easy to conflate:
SELECT type, queued, wait, totalq
FROM v$queue
WHERE type = 'DISPATCHER';
A nonzero, growing
QUEUED value, or a
WAIT figure climbing relative to
TOTALQ, means requests are backing up before a dispatcher can even hand them off to a client, the queue side view of the same contention the busy percentage above shows from the process side. The two rarely disagree for long: a dispatcher that is genuinely overloaded will usually show both a high busy percentage and a growing queue, while a dispatcher that is merely busy without anything actually waiting is probably fine as configured.
Correlating with Sessions
V$SESSION, filtered to Shared Server connections, ties contention back to who is actually generating the load rather than leaving it as an abstract process level number:
SELECT sid, username, server, status
FROM v$session
WHERE server = 'SHARED';
A large, steady count of sessions here alongside a rising dispatcher busy percentage is consistent load growth, the kind Lesson 2's sizing formula is meant to plan for. A sudden spike in this count that lines up exactly with the start of contention, by contrast, points toward a specific event, a batch job that should have used a dedicated connection as covered in Lesson 4, or a single misbehaving application, rather than organic growth that calls for more dispatchers.
Joining this to
V$CIRCUIT, covered in Lesson 8, goes one level further when a session count alone isn't enough: a circuit's
QUEUE column sitting on
COMMON for an unusually long time identifies which specific connection is actually stuck waiting, rather than just confirming that contention exists somewhere among a large session count.
Deciding What to Change
A high dispatcher busy percentage or a growing
V$QUEUE wait time both point toward adding dispatchers for that protocol, using the
DISPATCHERS parameter and the sizing formula from Lesson 2:
ALTER SYSTEM SET DISPATCHERS = '(PROTOCOL=TCP)(DISPATCHERS=4)';
Before assuming that's the whole fix, check
V$SHARED_SERVER's own
BUSY and
IDLE columns too. A dispatcher can look busy for two different reasons, and they call for different fixes. It might genuinely be under-provisioned for its own traffic, in which case more dispatchers is exactly right. Or the shared servers behind it can't keep up, so requests pile up waiting to be picked off the common queue, and the dispatcher itself spends more time holding and relaying those requests and less time idle, even though adding dispatchers wouldn't actually speed anything up. In that second case, raising
SHARED_SERVERS, or
MAX_SHARED_SERVERS if PMON is already capped at its ceiling, exactly the mechanism covered in Lesson 4, is the change that actually helps.
Persistent
WAIT figures that don't improve after adjusting either parameter are worth investigating at the network or session level instead, rather than continuing to add processes that were never the actual bottleneck. A slow network link between the database server and a remote client population, for instance, can produce wait times that look exactly like dispatcher contention but have nothing to do with how many dispatchers or shared servers are running.
A Real Example
This query and its output were captured directly from a running system rather than generated:
SELECT network "Protocol",
SUM(busy) / ( SUM(busy) + SUM(idle) ) "Total Busy Rate"
FROM
v$dispatcher
GROUP BY
network;
Protocol Total Busy Rate
-------- ---------------
tcp .5053635
tcps .0939372
Since instance startup, the TCP dispatchers have been busy about 50 percent of the time and the TCPS dispatchers about 9 percent, a healthy split for a system where most traffic arrives over plain TCP with a smaller amount over TLS. Neither number alone signals a problem; 50 percent busy with a flat, low
V$QUEUE wait time is an instance comfortably handling its load, not one approaching contention. The number only becomes actionable alongside the queue side check from earlier in this lesson, which is exactly why this lesson pairs the two views rather than treating either as sufficient on its own.
Putting It Together
A full contention check, in order, looks like this: query V$DISPATCHER for busy percentage by protocol; query V$QUEUE, filtered to TYPE = 'DISPATCHER', for queue depth and wait time on the same protocol; if both are elevated, check V$SHARED_SERVER to see whether the dispatcher or the shared servers behind it are the actual constraint; and only then decide between adding dispatchers, raising SHARED_SERVERS or MAX_SHARED_SERVERS, or looking outside Shared Server entirely at the network or a specific session. Skipping straight from "the dispatcher looks busy" to "add more dispatchers" is how you end up with a protocol that has plenty of dispatcher capacity and still shows the same contention it started with, because the actual bottleneck was never dispatchers to begin with.
Consider a TCP dispatcher showing 85 percent busy, a growing QUEUED value in V$QUEUE, and rising WAIT. The instinct is to add dispatchers. But a quick check of V$SHARED_SERVER shows every shared server also sitting near 90 percent busy, with none idle. Adding dispatchers here would let more requests reach the common queue faster, but they would simply pile up behind the same undersized pool of shared servers, making the queue backlog worse rather than better. Raising SHARED_SERVERS first, then reassessing whether dispatcher capacity is still a problem once shared servers are no longer the constraint, is the sequence that actually resolves this specific pattern, rather than the parameter this lesson's view happens to be named after.
By default, querying these views is limited to SYS and to other users with sufficient dictionary access, such as the SELECT_CATALOG_ROLE or SELECT ANY DICTIONARY privilege; an ordinary application account typically cannot run any of the queries in this lesson without one of those grants.
The next lesson examines the V$QUEUE view.
