Not every query on DB2 for z/OS uses an index. Sometimes the cheapest plan is to read the table's pages in order. Even when an index is used, prefetch decides whether those pages arrive one synchronous I/O at a time or in asynchronous bundles. This page covers table scans, sequential prefetch, sequential detection and dynamic prefetch, list prefetch, random access, RID lists and the RID pool, and hybrid join—the join method that is really a RID-list machine.
A synchronous read brings one page per I/O and the thread waits. Prefetch is Db2's way of saying “we will need a run of pages soon” and issuing asynchronous multi-page I/Os into the buffer pool ahead of the application. Elapsed time drops when prefetch guesses right. CPU and buffer steal can rise if prefetch reads pages nobody uses (for example a scan you abandon after ten rows without telling the optimizer).
IBM describes three prefetch styles you must be able to name in an interview and on a monitor report:
| Kind | PLAN_TABLE | Idea |
|---|---|---|
| Sequential | PREFETCH S | Read-ahead of consecutive pages for scans; bind-time intent |
| Dynamic | PREFETCH D (when shown) | Run-time sequential detection; prefetch sequential pages, sync I/O for random |
| List | PREFETCH L | RID list sorted by page; async multi-page reads of those pages |
| None / random | PREFETCH blank | Synchronous single-page I/O; typical for true random OLTP gets |
A table scan (PLAN_TABLE ACCESSTYPE = R, historically “relational scan”) reads the table's data pages. On a universal or segmented table space, Db2 scans pages that belong to that table, not every object that happens to share a space. ACCESSNAME is blank. Sequential prefetch is the normal I/O pattern (PREFETCH = S). Predicates are applied to each row as it is read—there is no index start/stop key.
When the optimizer prefers a scan:
A scan is not an insult from the optimizer. It is the wrong answer when you have a selective equal on a unique key and still see R—that usually means the predicate is not matching (stage 2, wrong type, or no index). OPTIMIZE FOR n ROWS or FETCH FIRST can discourage a scan-plus-prefetch plan when the application will stop after a handful of rows.
12345-- No WHERE: a table scan is expected on a modest table SELECT EMPNO, LASTNAME FROM HR.EMPLOYEE; -- PLAN_TABLE: ACCESSTYPE = 'R', PREFETCH = 'S', ACCESSNAME blank
Sequential prefetch is the scan workhorse. When the target table space (or sequential multi-table segmented access without an index) is entered, Db2 reads ahead by prefetch quantity—historically two prefetch quantities to start, then another quantity each time a trigger page is touched, trying to stay at least one quantity ahead. For 4 KB pages the quantity is often up to 32 pages (256 KB), depending on buffer pool VPSEQT and prefetch quantity settings.
Sequential prefetch is largely a bind-time choice for access paths that will read pages in physical order. If you only need 10 rows of a 10,000-row result, prefetch may still charge ahead unless OPTIMIZE FOR / FETCH FIRST told the optimizer the truth. Sequential prefetch and dynamic prefetch are different mechanisms; do not use the names interchangeably.
Sequential detection is the runtime algorithm that watches the pattern of page numbers being read. If pages look clustered or sequential, Db2 treats them as a sequence. If they jump around, they are random.
Dynamic prefetch uses that algorithm: multi-page asynchronous I/O for the sequential pages, synchronous I/O for the random ones. It is the usual prefetch for many index-to-data paths where clustering is mixed—some ranges of the key sit together on disk, some do not. Dynamic prefetch can turn on and off during one statement as the pattern changes.
This is why a “matching index” is not always random I/O. If the clustering index matches the predicate and CLUSTERRATIOF is high, data pages come in order and dynamic or sequential-style prefetch pays off. If the index is unclustered, you see more synchronous getpage waits unless list prefetch takes over.
Random access means the next page is not the neighbor of the last one. Typical case: unique index lookup of one row (a few index pages plus one data page), or nested loop inner-table lookups with unclustered keys. PLAN_TABLE PREFETCH is often blank. Accounting shows synchronous reads. Random is the right I/O for true singleton OLTP. It is the wrong I/O for “read 30% of the table through an unclustered index”—that is when list prefetch or a scan should have won.
List prefetch reads a set of data pages named by RIDs, not a physical sequential run. Db2 also uses it for non-consecutive index leaf pages (from non-leaf information) and for LOB pages (from the LOB map). It does not use sequential detection. Instead it:
PLAN_TABLE shows PREFETCH = L. List prefetch is attractive when qualified rows are not in clustering order, when skip-sequential access is sparse, or when DATAREPEATFACTORF says repeating the same pages would be expensive without a sorted RID list. Multiple-index access (ACCESSTYPE M) almost always involves list prefetch of the surviving RID list.
A RID is the record identifier of a row (page and slot). A RID list is a collection of those identifiers, often sorted, sometimes ANDed (intersection) or ORed (union) with another list. Building that list requires memory: the RID pool.
The RID pool is also used for hybrid joins, multiple index access, list prefetch, and some multi-row uniqueness enforcement. It is allocated as needed, up to the maximum.
When RID processing fails:
Accounting and Statistics “RID list” sections count these terminations. Non-zero RID failures on a workload that was supposed to list-prefetch are a tuning ticket: enlarge MAXRBLK, fix stats, or accept a scan.
1234567-- Selective OR of two indexed columns: candidate for MX / MI or MU + list prefetch SELECT EMPNO, LASTNAME FROM HR.EMPLOYEE WHERE WORKDEPT = 'A00' OR EMPNO = '000010'; -- Look for ACCESSTYPE M / MX / MI / MU and PREFETCH L
Hybrid join is PLAN_TABLE METHOD = 4. Think of it as a nested loop that does not probe the inner table once per outer row with random I/O. Instead Db2:
Hybrid join needs join columns (unlike nested loop, which can nest even for a Cartesian-style nest). It shines when the inner index is not well clustered: you still use the index to find RIDs, but I/O to data is list prefetch instead of a storm of synchronous inner gets. It consumes RID pool. If the RID list is huge or the pool is short, hybrid join can degrade—the same failure modes as other RID processing.
Nested loop and merge scan are covered in detail on the join methods page. Remember hybrid here because its performance story is prefetch and RIDs, not just “another join name.”
When you read a slow query:
Buffer pool VPSEQT (sequential steal threshold) and deferred write behavior affect how much prefetch you can absorb. Prefetch that fills the pool and then steals its own pages is a pool-sizing problem, not an optimizer insult.
A tablespace scan is reading every page of a picture book from page 1 to the end. Sequential prefetch is Mom bringing the next ten pages before you ask. Random access is jumping to page 47, then page 3, then page 90, one trip per page. Sequential detection is Mom noticing “oh, you are reading in order now” and starting to bring stacks, or “you are jumping again” and stopping. List prefetch is writing down all the page numbers you need, sorting them, and fetching those pages in handfuls. The RID pool is the scrap paper those page numbers are written on—if you run out of scrap paper, Mom gives up and reads the whole book. Hybrid join is using the index to write the scrap-paper list for the inner table, then fetching those pages in handfuls.
1. What does ACCESSTYPE R typically mean?
2. How does sequential prefetch differ from dynamic prefetch?
3. What does list prefetch use to decide which data pages to read?
4. Where are RID lists built, and what happens if the pool is too small?
5. Hybrid join (METHOD 4) is built around which prefetch idea?