Db2 for z/OS performance troubleshooting works best when you treat every complaint as a testable symptom. A slow screen, rising CPU graph, table-space scan, timeout, or failed utility is a starting point—not a diagnosis. This beginner-friendly guide shows how to collect evidence, compare it with a known-good period, and narrow the problem before changing SQL, indexes, subsystem settings, or application behavior.
Before tuning, define exactly what changed. Record the first known bad time, the last known good time, affected applications, Db2 subsystem or data-sharing member, SQL identity, package and collection, connection type, and business volume. “The query is slow” is too vague. “This statement rose from 200 milliseconds to 8 seconds after 14:00, while input volume stayed stable” gives you a comparison that can be tested.
12345678Symptom -> scope -> baseline -> Db2 evidence -> hypothesis -> controlled test For every incident, ask: 1. What became worse, and when? 2. Which SQL, object, thread, connection, or utility is involved? 3. What differs from the last healthy period? 4. Is time spent on CPU, I/O, locks, Db2 queues, or outside Db2? 5. Which single observation would prove or disprove the leading theory?
| Symptom | Capture first | Diagnostic question |
|---|---|---|
| Query suddenly slow | Elapsed time, class 1/class 2 time, SQL identity, access path, waits | Did execution, data, statistics, contention, or the access path change? |
| Index access became a table-space scan | Current and previous EXPLAIN rows, RUNSTATS history, package change | Why did the optimizer estimate that scanning was cheaper? |
| CPU or getpages increased | Accounting CPU, getpages, rows examined/returned, synchronous reads | Is Db2 doing more logical work, more physical I/O, or both? |
| Buffer-pool hit ratio dropped | Pool-level and object-level reads, writes, thresholds, residency | Which objects displaced useful pages, and did response time worsen? |
| Sort activity exploded | Sort counts, work-file use, rows sorted, EXPLAIN sort indicators | Did SQL shape, access path, cardinality, or memory pressure change? |
| Locks, threads, or DDF connections built up | Waiters and holders, unit-of-work age, thread state, DBAT and connection counts | What is holding progress, and why has it not committed or ended? |
A statement that was fast yesterday can be slow today even when its SQL text looks unchanged. The access path may have changed after RUNSTATS, REORG, index DDL, a bind, package invalidation, maintenance, or an application deployment. The path may be unchanged while input values now select far more rows. Contention, storage latency, CPU pressure, or a different member can also increase elapsed time without changing the optimizer's decision.
Start with accounting data for the affected execution. Compare elapsed time, general processor CPU, zIIP-eligible or consumed time where applicable, getpages, synchronous reads, prefetch activity, rows processed, sorts, lock/latch waits, and number of executions. Normalize totals per execution or per business transaction. A daily total can rise simply because the application did more work.
EXPLAIN records the optimizer's proposed access path in PLAN_TABLE and related tables. Use the same SQL, schema, special registers, parameter markers or representative literals, optimization environment, and catalog statistics when recreating a path. For static SQL, also inspect the package's captured access-path information and bind history available at your site. Do not assume a fresh ad hoc EXPLAIN exactly represents the package that ran during the incident.
123456789101112131415-- Simplified dynamic EXPLAIN example. -- Use the same qualifiers and relevant special-register context as the application. EXPLAIN PLAN SET QUERYNO = 4101 FOR SELECT O.ORDER_ID, O.ORDER_DATE, O.TOTAL_AMOUNT FROM APP.ORDERS O WHERE O.CUSTOMER_ID = ? AND O.ORDER_DATE >= ? ORDER BY O.ORDER_DATE DESC; -- Read your site's PLAN_TABLE with the columns supported at its Db2 level. SELECT QUERYNO, QBLOCKNO, PLANNO, METHOD, ACCESSTYPE, MATCHCOLS, ACCESSNAME, INDEXONLY, PREFETCH FROM PLAN_TABLE WHERE QUERYNO = 4101 ORDER BY QBLOCKNO, PLANNO;
| EXPLAIN area | What it describes | Troubleshooting clue |
|---|---|---|
| ACCESSTYPE | How Db2 reaches the data, such as index access or a scan | A changed value can explain a large increase in rows examined and getpages |
| MATCHCOLS | Leading index columns matched by predicates | Fewer matching columns often means less selective index navigation |
| INDEXONLY | Whether all required values can come from the index | Losing index-only access adds data-page visits |
| PREFETCH | Whether Db2 expects sequential, list, or dynamic prefetch | Useful context for scan behavior and synchronous read changes |
| SORT indicators | Whether Db2 plans sorts for joins, grouping, ordering, or distinct work | New sort indicators can predict work-file and CPU growth |
| Join sequence and method | Order and technique used to combine tables | A poor outer-table estimate can multiply work in later steps |
A table-space scan is not automatically wrong. If a query returns a large percentage of a table, sequential access with prefetch can cost less than jumping between an index and many data pages. The diagnostic question is not “How do I force the index?” but “What changed in Db2's cost estimate or in the available choices?”
If the scan is genuinely harmful, correct the cause: collect suitable statistics, rewrite a non-indexable predicate, restore or improve an index, or safely rebind after testing. Forcing yesterday's path can preserve a plan that no longer fits today's data.
CPU measures processor work. Getpages measure logical requests for Db2 pages. They are related but not identical to physical I/O. A page already resident in a buffer pool can satisfy many getpages without a disk read. Compare getpages per execution, rows examined versus rows returned, synchronous reads, prefetch reads, and CPU per execution.
Rising getpages with stable result rows often points to less selective access, fewer matching index columns, loss of index-only access, a changed join order, repeated probes, or poor clustering. Rising CPU without a similar getpage increase may suggest more sorting, expression evaluation, data conversion, compression work, or application calls. Both may rise when a query scans and filters far more rows. Confirm with EXPLAIN and accounting evidence rather than assigning every CPU increase to one SQL statement.
Hit ratio is the share of logical page requests satisfied without a new read, but an aggregate percentage is not a verdict. A deliberate sequential scan can lower a pool ratio while finishing efficiently. A high ratio can coexist with poor performance if the application repeatedly touches too many cached pages. Always correlate the ratio with synchronous I/O, read latency, getpages, writes, threshold events, and response time.
Use Db2 statistics monitoring to find the affected buffer pool, then move toward object-level evidence. Ask whether a new scan displaced frequently reused pages, whether one object grew, whether pool assignment changed, whether page size or sequential thresholds fit the workload, and whether storage became slower. Resize or reassign a pool only after proving capacity or isolation is the constraint. Otherwise, SQL that reads fewer pages is often the stronger fix.
A sort explosion means more sorts, larger sorts, or more concurrent sort work. ORDER BY, GROUP BY, DISTINCT, merge scan joins, duplicate removal, and some subqueries can require sorting. A useful index may previously have supplied the desired order. A join order or cardinality change may now create a huge intermediate result before sorting.
Compare accounting sort counts and records processed, statistics for the work-file database, and EXPLAIN sort indicators. Check SQL changes, access-path changes, data growth, inaccurate statistics, and concurrency. Work-file space or sort-memory tuning may relieve a proven resource bottleneck, but it does not repair a query that sorts millions of unnecessary rows.
Lock contention begins when a thread needs an incompatible lock held by another unit of work. A timeout means the wait exceeded its allowed duration. A deadlock means a cycle exists—for example, thread A holds row 1 and wants row 2 while thread B holds row 2 and wants row 1. Waiting longer cannot solve that cycle, so IRLM chooses a victim.
Capture the waiter, holder, object, partition, lock mode, SQL, authorization or correlation identity, connection type, and unit-of-work age. Look for infrequent commits, user think time inside a transaction, large updates, inconsistent object access order, lock escalation, and queries reading more rows than expected. The durable fix is usually shorter transactions, consistent access order, selective SQL, and intentional isolation—not a blanket timeout increase.
Thread buildup is a queueing symptom. Local allied threads and distributed DBATs are different populations, so identify the attachment type first. Inspect thread states: are they executing, waiting on locks, waiting for Db2 resources, inactive, or stalled in long units of work? A surge can come from slower SQL, a blocked transaction, application retry storms, a downstream outage, or traffic growth. Raising CTHREAD or MAXDBAT without finding the bottleneck may admit more work and make CPU, storage, locks, or another service collapse faster.
DDF also distinguishes network connections from active DBATs. Connection pooling and high-performance DBAT behavior can make counts differ from a simple one-client, one-thread model. Use DDF displays and your monitor to compare remote location, authorization ID, application name, connection count, active DBAT count, queued work, idle age, and transaction state. Coordinate with application owners to check pool maximums, leaked connections, retry policy, commit behavior, and whether requests are actually completing.
CHECK INDEX verifies consistency between index entries and table data. A nonzero result is not merely a performance statistic. Preserve the complete utility output, return code, messages, object identity, scope, and resulting restrictive state. Check preceding hardware, I/O, recovery, LOAD, REORG, or utility events. The corrective path depends on exactly what Db2 found and on the installed release.
Do not jump directly to REPAIR. First protect recoverability, understand whether the data or index is authoritative, and follow documented procedures. A rebuild may be appropriate for an index inconsistency, but the DBA must account for availability, image copies, related objects, pending states, and the original cause.
Read utility output from the first relevant error, not only the final failure code. Determine the phase and whether the problem is an unavailable input object, insufficient sort or work space, full target data set, authorization issue, I/O failure, duplicate key, damaged data, concurrent utility, or drain problem. Confirm the index and table space states before retrying.
Correct the named cause and use the documented restart or rerun behavior. Repeatedly submitting identical JCL can consume the window while preserving the same failure. After success, verify object state and consistency, and capture why the failure occurred so space estimates, utility controls, or scheduling can be corrected.
A utility requests a drain when it needs existing claims to leave and may need to prevent new incompatible claims. A drain timeout means an application claim remained too long. Find the claim holder, application owner, SQL, transaction age, and whether work is active or idle. A long cursor, uncommitted batch update, forgotten connection, or continuous application traffic can all block the utility.
Coordinate before canceling production work. The safest action may be a normal commit, ending an idle session, pausing traffic, or rescheduling the utility. Increasing UTIMOUT only allows a longer wait; it does not make the claim disappear. Also inspect retry behavior and the utility's options so repeated drain attempts do not create an avoidable application impact.
| Failure | Preserve and inspect | Safe direction |
|---|---|---|
| CHECK INDEX failure | Utility return code, messages, object state, inconsistent key details | Preserve output, determine whether data or index is inconsistent, then use the documented repair path |
| REBUILD INDEX failure | Phase, failing data set, space, sort, I/O, authorization, and object messages | Correct the named resource problem and restart according to utility guidance |
| Utility drain timeout | Utility messages, active claim holders, unit-of-work age, application owner | Resolve the holder or schedule conflict; do not merely extend the wait without evidence |
Imagine Db2 is a supermarket. A slow query is a shopper taking too long, but you should not immediately build a new checkout lane. First watch what happened. Did the shopper lose the aisle map and walk past every shelf? That is like losing an index and scanning. Did too many shoppers reach for the same freezer door? That is lock contention. Did carts fill every aisle? That resembles thread buildup. Did the cleaning crew wait because one shopper would not leave? That is a utility drain wait. EXPLAIN is the planned route through the store, while accounting and statistics show what the trip actually cost. Good troubleshooting compares the old trip with the new one before moving shelves.
1. What should you do first when a Db2 query is suddenly slow?
2. Why might Db2 change from index access to a table-space scan?
3. What does a getpage increase usually tell you?
4. Which evidence is most useful for a lock timeout?
5. What is the best first response to a utility drain timeout?
6. Why is an overall buffer-pool hit ratio insufficient by itself?
First establish the regression window and identify the exact SQL, package or statement token, workload, and subsystem member. Compare Db2 accounting data, elapsed and CPU time, getpages, reads, sorts, and lock waits against a healthy baseline. Then compare EXPLAIN access paths and verify changes to RUNSTATS, indexes, binds, data volume, predicates, and subsystem conditions.
Db2 chooses the eligible access path with the lowest estimated cost. It may favor a table-space scan after data growth, changed distribution statistics, stale or default statistics, a dropped or altered index, a predicate change, host-variable selectivity assumptions, or a bind and optimizer-level change. Compare old and new EXPLAIN evidence before forcing an index.
No. A getpage is a logical request for a page. The page can already be in a buffer pool, or Db2 may need I/O to read it. Compare getpages with synchronous and prefetch reads to distinguish extra logical processing from extra physical I/O.
Common causes include a changed access path, ORDER BY, GROUP BY, DISTINCT, merge scan joins, large intermediate results, inaccurate cardinality estimates, and lost index ordering. Use EXPLAIN sort indicators together with accounting sort counts and work-file statistics.
Capture both the waiter and holder, the locked object, lock mode, SQL, transaction age, commit behavior, and order in which resources are accessed. A deadlock is a cycle and requires a victim; a timeout is a wait that exceeded its limit without necessarily forming a cycle.
Keep the complete utility output and identify the object, inconsistency, return code, and resulting object state. Determine whether the index, data, or both require corrective action. Follow the documented Db2 procedure for the installed release and protect recoverability; do not use REPAIR casually.
The utility could not acquire the required drain because one or more application claims remained. Identify the claim holders, active SQL, and unit-of-work age, then coordinate a safe commit, application stop, or utility reschedule. Increasing UTIMOUT only changes how long the utility waits.
Read PLAN_TABLE evidence and understand how Db2 chooses SQL access paths
Understand lock waits, deadlock cycles, application claims, and utility drains
Learn how Db2 caches pages and how reads, writes, and thresholds affect workload
Connect remote connections, DBAT capacity, pooling, and distributed workload