1. O que é um índice e como funciona
Um índice no DB2 é uma estrutura de dados separada, organizada em árvore B+ (B-tree), que mantém as chaves de uma ou mais colunas em ordem e aponta para as páginas físicas onde as linhas estão armazenadas.
O custo de um índice tem dois lados:
| Operação | Com índice | Sem índice |
|---|---|---|
| SELECT por chave | Rápido — percorre a árvore | Lento — lê tudo |
| SELECT com range | Rápido se clustering | Lento — lê tudo |
| INSERT | Mais lento — atualiza o índice | Mais rápido |
| UPDATE na chave | Mais lento — reorganiza B-tree | Mais rápido |
| DELETE | Mais lento — remove do índice | Mais rápido |
2. Tipos de índice no DB2
| Tipo | Característica | Uso |
|---|---|---|
| Unique | Não permite valores duplicados na(s) coluna(s) | Chave primária, CPF, número de conta |
| Non-unique | Permite duplicatas | Colunas de busca frequente sem unicidade |
| Clustering | Determina a ordem física das linhas no tablespace | Coluna mais usada em range queries |
| Non-clustering | Só aponta para as linhas — não afeta ordem física | Buscas pontuais por outras colunas |
| Partitioned | Um índice por partição do tablespace | Tabelas muito grandes (bilhões de linhas) |
3. Clustering index — o mais importante
O clustering index é o único que influencia a ordem física dos dados no tablespace. Quando os dados estão fisicamente ordenados pela chave do clustering index, o DB2 pode ler faixas de linhas em muito menos operações de I/O.
-- Sem clustering por data: uma query de range lê páginas espalhadas SELECT * FROM TRANSACOES WHERE DT_TRANSACAO BETWEEN '2026-06-01' AND '2026-06-30'; -- → pode precisar de milhares de I/Os aleatórios -- Com clustering por DT_TRANSACAO: todas as linhas do mês estão -- juntas fisicamente → I/Os sequenciais, prefetch ativo → muito mais rápido
Cluster ratio
O cluster ratio mede o quanto os dados físicos ainda seguem a ordem do clustering index — de 0 a 1 (ou 0% a 100%). Com o tempo, INSERTs fora de ordem e DELETEs deixam o ratio cair.
| Cluster ratio | Situação | Ação |
|---|---|---|
| 0,95 – 1,00 | Excelente | Nenhuma |
| 0,80 – 0,94 | Aceitável | Monitorar |
| 0,60 – 0,79 | Degradado | Planejar REORG |
| < 0,60 | Crítico | REORG urgente |
CLUSTER, o DB2 aceita mas só o primeiro mantém efeito real na ordem física. O segundo vira non-clustering automaticamente.
4. Criando índices — sintaxe
-- Índice único (chave primária é criada automaticamente com unique index) CREATE UNIQUE INDEX SCHEMA1.XCONTA01 ON SCHEMA1.CONTAS (COD_CONTA); -- Clustering index por data de transação CREATE INDEX SCHEMA1.XTRANS01 ON SCHEMA1.TRANSACOES (DT_TRANSACAO) CLUSTER; -- Índice composto — ordem importa (ver seção 5) CREATE INDEX SCHEMA1.XTRANS02 ON SCHEMA1.TRANSACOES (COD_AGENCIA, DT_TRANSACAO); -- Índice com coluna descendente CREATE INDEX SCHEMA1.XTRANS03 ON SCHEMA1.TRANSACOES (DT_TRANSACAO DESC); -- Remover índice DROP INDEX SCHEMA1.XTRANS03;
X seguido do nome da tabela e um número sequencial — ex: XCONTA01, XCONTA02. Facilita identificar índices nas telas do DB2 e nos relatórios de EXPLAIN.
5. Índices compostos e matching columns
Um índice composto cobre mais de uma coluna. O conceito de matching columns define quantas colunas do índice o otimizador consegue usar para filtrar os dados.
-- 3 matching columns — usa o índice inteiro WHERE COD_AGENCIA = '001' AND COD_PRODUTO = 'CC' AND DT_TRANSACAO = '2026-06-15' -- 2 matching columns — usa as duas primeiras colunas do índice WHERE COD_AGENCIA = '001' AND COD_PRODUTO = 'CC' -- 1 matching column — usa só a primeira coluna WHERE COD_AGENCIA = '001' -- 0 matching columns — NÃO usa o índice (pula a primeira coluna) WHERE COD_PRODUTO = 'CC' AND DT_TRANSACAO = '2026-06-15'
=), depois as de range (BETWEEN, >, <). Colunas usadas em ORDER BY no final podem eliminar sort operations.
6. Quando o DB2 usa o índice
O otimizador do DB2 decide automaticamente se usa ou não um índice baseado em estatísticas e estimativas de custo. Há situações em que ele ignora o índice mesmo existindo.
O DB2 tende a usar o índice quando:
- O predicado usa a coluna-chave do índice com
=,>,<,BETWEEN,IN - A coluna tem alta seletividade — muitos valores distintos (ex: CPF, número de conta)
- As estatísticas estão atualizadas (RUNSTATS executado recentemente)
- O resultado esperado é uma fração pequena da tabela
O DB2 tende a ignorar o índice quando:
-- Função aplicada na coluna indexada → índice não é usado WHERE YEAR(DT_TRANSACAO) = 2026 -- Solução: usar range explícito WHERE DT_TRANSACAO BETWEEN '2026-01-01' AND '2026-12-31' -- LIKE com % no início → índice não é usado WHERE NOME LIKE '%SILVA' -- Funciona: % só no final WHERE NOME LIKE 'SILVA%' -- NOT ou != geralmente impedido WHERE STATUS != 'C' -- Solução: reformular com OR ou IN se possível WHERE STATUS IN ('A', 'P', 'S') -- Coluna com baixa seletividade → tablespace scan pode ser mais barato WHERE STATUS = 'A' -- se 90% das linhas são 'A', scan é mais eficiente
7. Quando criar e quando evitar
Crie um índice quando:
- A coluna aparece frequentemente em cláusulas
WHERE,JOIN ONouORDER BY - A coluna tem alta seletividade (muitos valores distintos)
- A tabela é grande e as queries retornam poucos registros (< 5–10% das linhas)
- A coluna é chave estrangeira usada em JOINs frequentes
Evite criar índice quando:
- A tabela é pequena — tablespace scan já é rápido o suficiente
- A coluna tem baixa seletividade (ex: STATUS com 3 valores possíveis)
- A tabela sofre muitos INSERTs em lote — cada índice extra aumenta o tempo do batch
- Já existe um índice composto que cobre o mesmo acesso como matching column
REBUILD INDEX — é muito mais rápido do que manter o índice atualizado linha a linha.
8. Manutenção — RUNSTATS e REORG
Dois utilitários são essenciais para manter os índices eficientes:
RUNSTATS
Coleta estatísticas de tabelas e índices e as grava no catálogo DB2 (SYSIBM.SYSINDEXES, SYSTABLESTATS). O otimizador usa essas estatísticas para escolher o melhor plano de acesso. Estatísticas desatualizadas levam o otimizador a tomar decisões erradas.
//RUNSTATS EXEC PGM=DSNUTILB,PARM='DB2P,RUNSTATS' //SYSPRINT DD SYSOUT=* //SYSIN DD * RUNSTATS TABLESPACE SCHEMA1.TSCONTAS TABLE(SCHEMA1.CONTAS) INDEX(ALL) SHRLEVEL REFERENCE /*
REORG INDEX
Reconstrói a estrutura B-tree do índice, eliminando páginas fragmentadas e restaurando o cluster ratio. Deve ser executado periodicamente ou quando o cluster ratio cai abaixo do limite aceitável.
//REORG EXEC PGM=DSNUTILB,PARM='DB2P,REORG' //SYSPRINT DD SYSOUT=* //SYSIN DD * REORG TABLESPACE SCHEMA1.TSCONTAS UNLOAD CONTINUE SHRLEVEL NONE /*
SHRLEVEL NONE coloca o tablespace em modo exclusivo durante o REORG — sem acesso de leitura ou escrita. Em produção 24x7, use SHRLEVEL REFERENCE (permite leitura) ou SHRLEVEL CHANGE (permite leitura e escrita, com mais overhead). Combine sempre REORG + RUNSTATS para que as novas estatísticas reflitam o estado reorganizado.
9. Impacto no COBOL embutido
A escolha do índice afeta diretamente o desempenho de programas COBOL que usam SQL embutido. Algumas práticas importantes:
* Índice em COD_CONTA (chave primária) — 1 matching column
EXEC SQL
SELECT SALDO, STATUS
INTO :WS-SALDO, :WS-STATUS
FROM CONTAS
WHERE COD_CONTA = :WS-COD-CONTA
END-EXEC
* EVITE: YEAR() na coluna indexada DT_ABERTURA EXEC SQL SELECT COUNT(*) INTO :WS-QTD FROM CONTAS WHERE YEAR(DT_ABERTURA) = :WS-ANO END-EXEC * PREFIRA: range explícito — usa o índice EXEC SQL SELECT COUNT(*) INTO :WS-QTD FROM CONTAS WHERE DT_ABERTURA BETWEEN :WS-DT-INI AND :WS-DT-FIM END-EXEC
* Clustering index em DT_TRANSACAO — ORDER BY aproveita a ordem física * e elimina operação de sort interna EXEC SQL DECLARE CURSOR-TRANS CURSOR FOR SELECT COD_CONTA, VL_TRANSACAO, DT_TRANSACAO FROM TRANSACOES WHERE COD_AGENCIA = :WS-AGENCIA AND DT_TRANSACAO BETWEEN :WS-DT-INI AND :WS-DT-FIM ORDER BY DT_TRANSACAO END-EXEC