DB2 for z/OS — Tablespaces, Indexes, Buffer Pools, Stored Procedures & Query Optimization
DB2 for z/OS is IBM’s relational database management system for the mainframe — it powers the world’s largest transaction processing systems at banks, airlines, insurance companies, and government agencies, processing tens of thousands of SQL statements per second with 99.999% availability.
What You’ll Learn
- Creating tables, views, indexes, and authorizations with DB2 SQL
- Tablespace types: simple, segmented, and partitioned
- Index strategies: clustering, non-clustering, and partition indexes
- Buffer pool tuning with VPAR and Hiperpool
- Writing and deploying stored procedures on z/OS
- BIND and REBIND for plan/package management
- Deadlock detection with IRLM
- EXPLAIN for query optimization
Why DB2 for z/OS Matters
Mainframes handle 68% of the world’s production IT workloads, and DB2 is the primary database driving those systems. Unlike distributed databases, DB2 for z/OS is designed for mixed workloads — thousands of concurrent online transactions running alongside massive batch jobs — without compromising performance or data integrity.
Durga Antivirus Pro uses DB2-style buffer pool management for caching threat signatures, ensuring frequently accessed patterns are served from memory. DodaZIP applies DB2 partitioned tablespace concepts when processing multi-volume compressed archives.
Learning Path
flowchart LR
A["Mainframe Overview"] --> B["COBOL Programming"]
B --> C["JCL & CICS"]
C --> D["DB2 for z/OS<br/>You are here"]
D --> E["Advanced DB2"]
style D fill:#f90,color:#fff
DB2 Objects Hierarchy
flowchart TB
SUBSYSTEM["DB2 Subsystem"] --> DB["Database"]
DB --> TS["Tablespace"]
TS --> TBL["Table"]
TBL --> IX["Index"]
TBL --> COL["Columns"]
TBL --> ROW["Rows"]
BUFF["Buffer Pool"] --> TS
SQL in DB2 for z/OS
DB2 supports standard SQL with mainframe-specific extensions.
DDL — Data Definition Language
-- Create a database
CREATE DATABASE BANKING
BUFFERPOOL BP0
INDEXBP BP1;
-- Create a segmented tablespace
CREATE TABLESPACE CUST_TS
IN BANKING
USING STOGROUP SG01
PRIQTY 1440
SECQTY 720
SEGSIZE 32
LOCKSIZE PAGE;
-- Create a table
CREATE TABLE BANKING.CUSTOMER (
CUST_ID CHAR(10) NOT NULL,
FIRST_NAME VARCHAR(50),
LAST_NAME VARCHAR(50),
BALANCE DECIMAL(15,2),
STATUS CHAR(1),
CREATED_TS TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP,
PRIMARY KEY (CUST_ID)
)
IN BANKING.CUST_TS;
-- Create an index
CREATE UNIQUE INDEX CUST_PK
ON BANKING.CUSTOMER (CUST_ID)
CLUSTER
BUFFERPOOL BP1;DML — Data Manipulation Language
-- Insert with host variables (embedded in COBOL)
EXEC SQL
INSERT INTO BANKING.CUSTOMER
(CUST_ID, FIRST_NAME, LAST_NAME, BALANCE, STATUS)
VALUES (:CUST-ID, :FIRST-NAME, :LAST-NAME, :BALANCE, :STATUS)
END-EXEC.
-- Query with cursor
EXEC SQL
DECLARE CUST_CURSOR CURSOR FOR
SELECT CUST_ID, LAST_NAME, BALANCE
FROM BANKING.CUSTOMER
WHERE STATUS = 'A'
ORDER BY BALANCE DESC
END-EXEC.
EXEC SQL OPEN CUST_CURSOR END-EXEC.
EXEC SQL FETCH CUST_CURSOR INTO :CUST-ID, :LAST-NAME, :BALANCE END-EXEC.DCL — Data Control Language
-- Grant access
GRANT SELECT, INSERT, UPDATE
ON BANKING.CUSTOMER
TO USER1;
GRANT EXECUTE
ON PLAN BANKPLAN
TO PUBLIC;
-- Use roles for consistent authorization
CREATE ROLE DB_ADMIN;
GRANT DBADM ON DATABASE BANKING TO ROLE DB_ADMIN;
GRANT ROLE DB_ADMIN TO USER2;Tablespace Types
Choosing the right tablespace type is critical for performance.
| Type | Best For | Characteristics |
|---|---|---|
| Simple | Small, static tables | No segmentation, entire tablespace is one unit |
| Segmented | Most OLTP tables | Data divided into 4-64 pages per segment. Individual segments can be locked |
| Partitioned | Very large tables (billions of rows) | Data split into partitions by key range. Each partition has its own index |
When to Use Each
- Simple: Tables under 100 pages that never grow. Rarely used in new designs.
- Segmented: Default choice for OLTP. Good row-level locking, efficient space management.
- Partitioned: Tables over 10 GB. Partition pruning dramatically reduces I/O.
Index Strategies
| Index Type | Purpose | Example |
|---|---|---|
| Clustering | Physically orders rows on disk by index key | CUSTOMER by CUST_ID |
| Non-clustering | Fast lookup without changing physical order | Secondary index on LAST_NAME |
| Partition | Local index within a partitioned tablespace | Per-partition indexes for parallel access |
| Data-partitioned secondary | Global secondary index over partitioned table | Cross-partition lookups |
Index Design Example
-- Primary clustering index (physical order = logical order)
CREATE UNIQUE INDEX CUST_PK
ON BANKING.CUSTOMER (CUST_ID ASC)
CLUSTER
BUFFERPOOL BP1
PCTFREE 10;
-- Secondary index for name searches
CREATE INDEX CUST_NAME_IDX
ON BANKING.CUSTOMER (LAST_NAME ASC, FIRST_NAME ASC)
BUFFERPOOL BP2;Buffer Pools (VPAR, Hiperpool)
Buffer pools determine how much data stays in memory. DB2 uses virtual buffer pools and hiperpools for different performance tiers.
BP0 - Default 4 KB buffer pool (catalog and directory)
BP1 - Primary index buffer pool (4 KB)
BP2 - Data buffer pool (4 KB)
BP32K - Large buffer pool (32 KB pages)VPAR (Variable Page Size)
Different tablespaces can use different page sizes (4 KB, 8 KB, 16 KB, 32 KB). Match page size to row size — a table with 300-byte rows fits 13 rows per 4 KB page but 54 rows per 16 KB page.
Hiperpool
An expanded storage layer that acts as a second-level cache between virtual buffer pools and disk. Hiperpools are less common with modern z/OS but still used in high-performance installations.
-- Assign tablespace to a specific buffer pool
ALTER TABLESPACE BANKING.CUST_TS
BUFFERPOOL BP2;Stored Procedures
DB2 for z/OS stored procedures run as z/OS workload manager (WLM) address spaces. They can be written in COBOL, PL/I, C, or Java.
-- Create a COBOL stored procedure
CREATE PROCEDURE GET_CUSTOMER_BALANCE (
IN CUST_ID CHAR(10),
OUT BALANCE DECIMAL(15,2)
)
LANGUAGE COBOL
EXTERNAL NAME GETBAL
WLM ENVIRONMENT DSNWLM
PARAMETER STYLE GENERAL
COMMIT ON RETURN YES;
-- Call it from SQL
CALL GET_CUSTOMER_BALANCE('CUST001', ?);In COBOL:
IDENTIFICATION DIVISION.
PROGRAM-ID. GETBAL.
DATA DIVISION.
WORKING-STORAGE SECTION.
EXEC SQL INCLUDE SQLCA END-EXEC.
LINKAGE SECTION.
01 LS-CUST-ID PIC X(10).
01 LS-BALANCE PIC 9(7)V99.
PROCEDURE DIVISION USING LS-CUST-ID LS-BALANCE.
EXEC SQL
SELECT BALANCE INTO :LS-BALANCE
FROM BANKING.CUSTOMER
WHERE CUST_ID = :LS-CUST-ID
END-EXEC.
GOBACK.Plan/Package Management (BIND, REBIND)
BIND is the process of preparing SQL statements for execution. It creates an access path (query plan) determined by DB2’s optimizer.
BIND PLAN(BANKPLAN) PKLIST(BANKPKG.*) - Bind a plan
BIND PACKAGE(BANKPKG) MEMBER(PGM001) - Bind a package into a plan
REBIND PLAN(BANKPLAN) - Regenerate access paths
FREE PLAN(BANKPLAN) - Remove planBIND Parameters
BIND PACKAGE(BANKPKG) -
MEMBER(PGM001) -
ACTION(REPLACE) -
EXPLAIN(YES) -
ISOLATION(CS) -
VALIDATE(BIND)| Parameter | Meaning |
|---|---|
| ISOLATION(CS) | Cursor Stability — read committed |
| ISOLATION(RR) | Repeatable Read — holds all locks |
| EXPLAIN(YES) | Generate EXPLAIN output for analysis |
| VALIDATE(BIND) | Validate all SQL at bind time |
Deadlock Detection (IRLM)
IRLM (Internal Resource Lock Manager) is DB2’s deadlock detection subsystem. It runs as a separate address space and monitors lock contention.
How Deadlocks Happen
Transaction A: Locks row 1, waits for row 2
Transaction B: Locks row 2, waits for row 1
→ IRLM detects the cycle and terminates one transactionDeadlock Resolution
-901 SQLCODE -911: Deadlock or timeout. Current transaction is rolled back.
-913 SQLCODE -913: Deadlock. Transaction is chosen as the victim.Prevention Strategies
- Access tables in the same order in all programs
- Use COMMIT frequently to release locks
- Use ISOLATION(CS) instead of ISOLATION(RR) where possible
- Keep transactions short
EXPLAIN for Query Optimization
EXPLAIN shows the access path DB2’s optimizer chose for your SQL. Use it to detect full table scans, poor index usage, and suboptimal join methods.
EXPLAIN ALL SET QUERYNO = 1 FOR
SELECT C.LAST_NAME, C.BALANCE, A.ACCOUNT_TYPE
FROM BANKING.CUSTOMER C
JOIN BANKING.ACCOUNT A ON C.CUST_ID = A.CUST_ID
WHERE C.STATUS = 'A';After BIND, query the plan table:
SELECT QBLOCKNO, ACCESSNAME, METHOD,
TABNO, MATCHCOLS, PREFETCH, SORTC_GROUP
FROM PLAN_TABLE
WHERE QUERYNO = 1
ORDER BY QBLOCKNO;| Column | Meaning |
|---|---|
| ACCESSNAME | Index name used (blank = tablespace scan) |
| METHOD | 0 = direct, 1 = nested loop join, 2 = merge scan join, 3 = sort |
| MATCHCOLS | Number of index columns used for matching |
| PREFETCH | Sequential, list, or dynamic prefetch |
| SORTC_GROUP | Sort needed for GROUP BY |
Red Flags in EXPLAIN Output
- ACCESSNAME = blank with large table — full tablespace scan
- MATCHCOLS = 0 with an index — index scan but no matching columns used
- METHOD = 3 (sort) with large result — consider an index that eliminates the sort
DB2I
DB2 Interactive (DB2I) is the ISPF-based environment for DB2 administration. Access it from the ISPF main menu by selecting option 7 (DSN) or running the DB2I CLIST.
DB2I Primary Option Menu
1. SPUFI - Execute SQL statements
2. DSN - Process SQL and bind
3. UTILITIES - RUNSTATS, REORG, LOAD, UNLOAD
4. COMMANDS - DB2 operator commands
5. PROGRAM PREPARATION - Precompile, compile, bind
6. DCLGEN - Generate COBOL declarations from tablesCommon Errors
1. SQLCODE -911 (Deadlock/timeout)
Transaction X is chosen as the victim. Retry the transaction. Deadlocks are normal in high-concurrency systems — implement retry logic.
2. SQLCODE -904 (Resource unavailable)
A resource (tablespace, buffer pool) is unavailable. Check IRLM status, buffer pool definitions, and STOGROUP availability.
3. SQLCODE -117 (Column not in same tablespace)
A table with multiple tablespaces has a column in the wrong tablespace. Check table DDL — all columns of an index must be in the same tablespace.
4. SQLCODE -803 (Duplicate key)
Trying to insert a row with a duplicate primary key or unique index value. Check for existing data or use MERGE instead of INSERT.
5. BIND fails — “No valid access path”
The optimizer cannot find an access path. This usually means no index exists for the WHERE clause. Create an index and REBIND.
6. REBIND caused performance regression
A new access path is worse than the old one. Use REBIND PLAN(BANKPLAN) RESTORE to revert to the previous plan.
Practice Questions
What is the difference between a simple and segmented tablespace? Simple tablespaces are single-unit with no internal partitioning. Segmented tablespaces split data into segments of 4-64 pages, allowing segment-level locking and better space management.
What does IRLM do? IRLM (Internal Resource Lock Manager) detects and resolves deadlocks between concurrent DB2 transactions.
How do you check the access path DB2 chose for a query? Use EXPLAIN ALL SET QUERYNO = N FOR … then query PLAN_TABLE to see the chosen access path, indexes, join methods, and sort requirements.
What is the difference between BIND and REBIND? BIND creates the initial access path for SQL statements. REBIND regenerates access paths — used after RUNSTATS or when adding indexes to improve performance.
What is a clustering index? An index that physically orders rows on disk to match the index key order. Queries that access rows in key order benefit from sequential prefetch.
Challenge: A banking application has a CUSTOMER table with 50 million rows and an ACCOUNT table with 200 million rows. Design the tablespace and index strategy: (1) choose tablespace type for each table, (2) design primary and secondary indexes, (3) define buffer pool assignments, (4) create a stored procedure to transfer funds between accounts with COMMIT handling to minimize deadlock risk.
FAQ
Try It Yourself
Simulate DB2 EXPLAIN analysis with Python:
# Simulate DB2 EXPLAIN output analysis
explain_rows = [
{"qblockno": 1, "tabno": 1, "accessname": "CUST_PK", "method": 0, "matchcols": 1, "prefetch": "SEQUENTIAL"},
{"qblockno": 2, "tabno": 2, "accessname": "ACCT_CUST_FK", "method": 1, "matchcols": 1, "prefetch": "LIST"},
]
def analyze_explain(rows):
for row in rows:
issues = []
if not row["accessname"]:
issues.append("FULL TABLESPACE SCAN - add index")
if row["matchcols"] == 0 and row["accessname"]:
issues.append("Index used but no matching columns - check WHERE clause")
if row["method"] == 3:
issues.append("Sort required - consider sorted index")
if issues:
print(f"QBLOCK {row['qblockno']}, TAB {row['tabno']}:")
for issue in issues:
print(f" ⚠ {issue}")
else:
print(f"QBLOCK {row['qblockno']}, TAB {row['tabno']}: ✓ Efficient access path")
analyze_explain(explain_rows)Expected output:
QBLOCK 1, TAB 1: ✓ Efficient access path
QBLOCK 2, TAB 2: ✓ Efficient access pathWhat’s Next
| Tutorial | What You’ll Learn |
|---|---|
| COBOL with DB2 | Embedded SQL in COBOL programs |
| CICS Transaction Processing | CICS-DB2 integration |
| SQL Fundamentals | SQL basics for distributed databases |
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-20.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro