Este lab é dedicado à compressão de dados de tabelas e índices no Db2. O foco está em tabelas com organização por linha, ilustrando decisões práticas entre COMPRESS YES STATIC, COMPRESS YES ADAPTIVE, VALUE COMPRESSION e compressão de índices.
1. Objetivos do lab
No final deverá conseguir:
- Distinguir as opções atuais de compressão para tabelas com organização de dados por linha no Db2
- Estimar ganhos em tabelas sem compressão ativa
- Estimar ganhos adicionais em tabelas que já estão comprimidas
- Estimar ganhos potenciais para índices sem compressão
- Ativar compressão para tabelas e índices
- Reorganizar os objetos para materializar a compressão
2. Software necessário e como obtê-lo
Para este lab use preferencialmente:
- Db2 LUW 12.1 ou 11.5.x (Community Edition serve) em Linux
- Uma conta com privilégios de proprietário da instância
Pode obter o IBM Db2 através do canal oficial da IBM para downloads do produto
3. Opções atuais de compressão em tabelas de dados organizados por linha
Quando a tabela é organizada por linha, as opções relevantes são as seguintes:
| Opção | Cláusula típica | Uso principal |
|---|---|---|
| Classic row compression | COMPRESS YES STATIC | Dicionário estável, útil quando quer uma compressão previsível após REORG |
| Adaptive compression | COMPRESS YES ADAPTIVE | Dicionário adaptativo, normalmente a opção mais interessante para tabelas OLTP com evolução de dados contínua |
| Value compression | ACTIVATE VALUE COMPRESSION | Remove armazenamento redundante de NULL, strings vazias e valores repetidos em colunas específicas |
| System default compression | ALTER col COMPRESS SYSTEM DEFAULT | Evita armazenar explicitamente valores default do sistema, como zeros ou blanks |
| Index compression | ALTER INDEX ... COMPRESS YES | Reduz o espaço físico dos índices, em especial em ambientes OLTP e DW com muitos prefixos repetidos |
Na prática:
- Compressão estática e adaptativa não podem ser usadas simultaneamente na mesma tabela
- Value compression pode ser combinada com um dos dois modos de row compression
- A compressão de índices é controlada separadamente
O que distingue STATIC de ADAPTIVE
Em ambos os casos, o Db2 reduz o espaço ocupado pelas linhas substituindo padrões de dados repetidos por símbolos que ocupam menos espaço. A diferença real está em onde o dicionário de compressão é estabelecido e com que frequência ele se consegue adaptar ao padrão real dos dados.
COMPRESS YES STATIC
Na compressão estática, o Db2 usa apenas um dicionário global. Esse dicionário tenta capturar os padrões mais repetidos encontrados numa amostra representativa da tabela toda.
- O dicionário global pode ser criado automaticamente após um determinado “threshold” de dados inseridos ser atingido ou criado através de REORG.
- Depois de criado, esse dicionário não se ajusta continuamente aos padrões locais de dados de cada página
STATIC costuma funcionar bem quando:
- A tabela tem padrões de linha bastante repetitivos
- Os valores mais comuns mudam pouco ao longo do tempo
COMPRESS YES ADAPTIVE
Na compressão adaptativa, o Db2 usa duas camadas de compressão:
- O mesmo dicionário global já usado na compressão estática
- dicionários adicionais ao nível de páginas específicas que permitem comprimir de forma eficaz padrões de repetição locais.
O Db2 pode criar de forma dinâmica dicionários de compressão ao nível de página, com base nos padrões repetidos existentes nessa página. Se necessário, esse dicionário local pode ser recriado automaticamente mais tarde.
O dicionário global continua a existir e continua a comprimir padrões globais. O ganho adicional do modo adaptativo de uma segunda camada de compressão, que captura a repetição local que o dicionário global não consegue representar tão bem e substitui os valores que repetem localmente por símbolos mapeados através de dicionário de página.
Compressão adaptativa costuma ser uma boa aposta quando:
- Existem muitas colunas opcionais.
- Há cadeias de texto com repetição parcial, mas não totalmente uniforme ao longo da tabela.
- O perfil dos dados muda com o tempo, por exemplo por canais, campanhas, estados operacionais ou novos tipos de carga.
Imagine uma tabela de encomendas como a deste lab:
- Algumas páginas ficam dominadas por encomendas
ONLINE - Outras acumulam mais encomendas
PARTNER - Certos blocos têm mais
remarks,promo_codeegift_message - Outros têm combinações diferentes de
shipping_mode,sales_repouapproval_code
Este lab não estabelece compressão adaptativa como primeira escolha. Primeiro mede:
PCTPAGESSAVED_STATICPCTPAGESSAVED_ADAPTIVE
E só depois decide. Se a diferença for irrelevante, então o dicionário global já está a fazer a maior parte do trabalho e a compressão estática pode ser suficiente. Se a diferença for significativa, isso é um sinal de que a compressão adaptativa consegue explorar repetição local adicional para obter uma compressão mais eficiente.
E as tabelas com organização de dados por coluna?
As tabelas com organização de dados por coluna têm mecanismos próprios de compressão. Esse tema é importante, mas não é o âmbito deste lab.
Para este artigo:
- Vamos trabalhar apenas com tabelas cujos dados são organizados por linha (
TABLEORG = 'R') - A função
SYSPROC.ADMIN_GET_TAB_COMPRESS_INFOé específica de row compression e devolve zero linhas para tabelas column-organized
4. Criar a base de dados do lab
Antes de criar a base de dados, mudado-nos para a conta proprietária da instância Db2.
Exemplo:
su - db2inst1Se no seu ambiente o utilizador proprietário da instância tiver outro nome, substitua db2inst1 pelo nome correto.
Vamos executar, de forma a criar a base de dados:
db2 "create database COMPLAB pagesize 32768"5. Criar os objetos do schema
Vamos criar um os objetos de um cenário de negócio simples mas suficiente para mostrar compressão em dados e índices.
Além das tabelas principais, vamos criar uma tabela pequena e uma tabela volatile que serão excluídas de análise de compressão.
cat > /tmp/complab_schema.sql <<'SQL'
CONNECT TO COMPLAB;
CREATE SCHEMA biz;
CREATE TABLE biz.customer_master
(
customer_id INTEGER NOT NULL,
customer_name VARCHAR(80) NOT NULL,
segment VARCHAR(20) NOT NULL,
country_code CHAR(2) NOT NULL,
region_name VARCHAR(20) NOT NULL,
email_address VARCHAR(120),
phone_number VARCHAR(24),
marketing_opt_in SMALLINT NOT NULL DEFAULT 0,
credit_class CHAR(1) NOT NULL DEFAULT 'B',
status_code CHAR(1) NOT NULL DEFAULT 'A',
comments VARCHAR(160),
created_ts TIMESTAMP NOT NULL,
filler_code CHAR(12) NOT NULL DEFAULT 'STANDARD',
PRIMARY KEY (customer_id)
);
CREATE TABLE biz.sales_order
(
order_id BIGINT NOT NULL,
customer_id INTEGER NOT NULL,
order_date DATE NOT NULL,
channel_code VARCHAR(12) NOT NULL,
order_status VARCHAR(16) NOT NULL,
country_code CHAR(2) NOT NULL,
currency_code CHAR(3) NOT NULL,
payment_method VARCHAR(20) NOT NULL,
shipping_mode VARCHAR(20) NOT NULL,
sales_rep VARCHAR(30) NOT NULL,
promo_code VARCHAR(20),
approval_code CHAR(12),
gift_message VARCHAR(120),
remarks VARCHAR(200),
fraud_score SMALLINT NOT NULL DEFAULT 0,
total_amount DECIMAL(13,2) NOT NULL,
last_update_ts TIMESTAMP NOT NULL,
PRIMARY KEY (order_id)
);
CREATE TABLE biz.sales_order_line
(
line_id BIGINT NOT NULL,
order_id BIGINT NOT NULL,
line_no SMALLINT NOT NULL,
sku_code VARCHAR(30) NOT NULL,
product_family VARCHAR(30) NOT NULL,
warehouse_code CHAR(4) NOT NULL,
tax_code CHAR(3) NOT NULL,
quantity SMALLINT NOT NULL,
unit_price DECIMAL(11,2) NOT NULL,
discount_pct DECIMAL(5,2) NOT NULL DEFAULT 0,
serial_number VARCHAR(40),
line_comment VARCHAR(120),
backorder_flag SMALLINT NOT NULL DEFAULT 0,
fulfilled_flag SMALLINT NOT NULL DEFAULT 1,
PRIMARY KEY (line_id)
);
CREATE TABLE biz.small_reference
(
ref_code CHAR(4) NOT NULL,
ref_group VARCHAR(20) NOT NULL,
ref_description VARCHAR(80) NOT NULL,
PRIMARY KEY (ref_code)
);
CREATE TABLE biz.session_work_queue
(
queue_id BIGINT NOT NULL,
session_token CHAR(24) NOT NULL,
worker_name VARCHAR(30) NOT NULL,
payload_type VARCHAR(20) NOT NULL,
payload_ref VARCHAR(40),
status_code CHAR(1) NOT NULL,
created_ts TIMESTAMP NOT NULL,
PRIMARY KEY (queue_id)
);
ALTER TABLE biz.session_work_queue VOLATILE CARDINALITY;
CREATE TABLE biz.tab_comp_baseline
(
tabname VARCHAR(128) NOT NULL,
pre_npages INTEGER NOT NULL,
pagesize INTEGER NOT NULL,
st_pct SMALLINT NOT NULL,
ad_pct SMALLINT NOT NULL,
st_mb DECIMAL(20,2) NOT NULL,
ad_mb DECIMAL(20,2) NOT NULL,
PRIMARY KEY (tabname)
);
CREATE INDEX biz.ix_order_customer_date
ON biz.sales_order (customer_id, order_date);
CREATE INDEX biz.ix_order_status_country
ON biz.sales_order (order_status, country_code);
CREATE INDEX biz.ix_order_salesrep
ON biz.sales_order (sales_rep);
CREATE INDEX biz.ix_line_order
ON biz.sales_order_line (order_id);
CREATE INDEX biz.ix_line_sku
ON biz.sales_order_line (sku_code);
CREATE INDEX biz.ix_line_wh_family
ON biz.sales_order_line (warehouse_code, product_family);
CREATE INDEX biz.ix_queue_status
ON biz.session_work_queue (status_code, created_ts);
COMMIT;
CONNECT RESET;
SQL
db2 -tvf /tmp/complab_schema.sql6. Gerar e carregar dados representativos
Vamos carregar dados com padrões típicos de negócio:
- Códigos de país, canais, métodos de pagamento e estados com forte repetição
- Colunas opcionais com muitos
NULL - Textos semi-repetitivos
- Volume suficiente para que a compressão mostre resultados visíveis
- Uma tabela pequena e uma tabela
VOLATILEque não justificam avaliação
6.1 Carregar customer_master
cat > /tmp/complab_load_customers.sql <<'SQL'
CONNECT TO COMPLAB;
INSERT INTO biz.customer_master
WITH d(n) AS
(
VALUES 0,1,2,3,4,5,6,7,8,9
),
nums(n) AS
(
SELECT
a.n * 10000 +
b.n * 1000 +
c.n * 100 +
d1.n * 10 +
e.n + 1
FROM d a, d b, d c, d d1, d e
WHERE
a.n * 10000 +
b.n * 1000 +
c.n * 100 +
d1.n * 10 +
e.n + 1 <= 100000
)
SELECT
n,
'Customer ' || VARCHAR(n),
CASE MOD(n, 5)
WHEN 0 THEN 'Enterprise'
WHEN 1 THEN 'Corporate'
WHEN 2 THEN 'SMB'
WHEN 3 THEN 'Retail'
ELSE 'Partner'
END,
CASE MOD(n, 6)
WHEN 0 THEN 'PT'
WHEN 1 THEN 'ES'
WHEN 2 THEN 'FR'
WHEN 3 THEN 'DE'
WHEN 4 THEN 'IT'
ELSE 'NL'
END,
CASE MOD(n, 6)
WHEN 0 THEN 'Iberia'
WHEN 1 THEN 'Iberia'
WHEN 2 THEN 'France'
WHEN 3 THEN 'DACH'
WHEN 4 THEN 'SouthEU'
ELSE 'Benelux'
END,
CASE
WHEN MOD(n, 20) = 0 THEN NULL
ELSE 'contact' || VARCHAR(MOD(n, 12000)) || '@example.com'
END,
CASE
WHEN MOD(n, 15) = 0 THEN NULL
ELSE '+351210' || RIGHT(DIGITS(100000 + MOD(n, 900000)), 6)
END,
CASE WHEN MOD(n, 4) = 0 THEN 1 ELSE 0 END,
CASE MOD(n, 4)
WHEN 0 THEN 'A'
WHEN 1 THEN 'B'
WHEN 2 THEN 'B'
ELSE 'C'
END,
CASE WHEN MOD(n, 25) = 0 THEN 'H' ELSE 'A' END,
CASE
WHEN MOD(n, 7) = 0 THEN 'Annual contract customer'
WHEN MOD(n, 11) = 0 THEN 'Prefers consolidated monthly invoice'
ELSE 'Standard sales account'
END,
TIMESTAMP('2023-01-01-08.00.00') + MOD(n, 700) DAYS,
CASE WHEN MOD(n, 10) = 0 THEN 'STANDARD' ELSE 'STANDARD' END
FROM nums
WHERE NOT EXISTS
(
SELECT 1
FROM biz.customer_master c
WHERE c.customer_id = nums.n
);
COMMIT;
CONNECT RESET;
SQL
db2 -tvf /tmp/complab_load_customers.sql6.2 Carregar sales_order
Este bloco de código cria 500000 encomendas com muitos padrões repetidos e algumas colunas esparsas. O carregamento é feito em lotes de 25000 linhas com COMMIT entre lotes, para evitar SQL0964C em ambientes com configuração de log mais conservadora. O NOT EXISTS torna a carga repetível sem gerar erros de chave duplicada.
Dentro de cada lote de 25000 linhas, criamos micro-lotes locais de 250 encomendas. Em cada micro-lote, várias colunas textuais longas repetem os mesmos padrões locais e o mesmo identificador local várias vezes, mas esses padrões mudam no micro-lote seguinte. Fazemos isso porque estamos a tentar reproduzir um cenário em que compressão adaptativa seja mais vantajosa que compressão estática.
db2 connect to COMPLAB
for start_n in 1 25001 50001 75001 100001 125001 150001 175001 200001 225001 250001 275001 300001 325001 350001 375001 400001 425001 450001 475001
do
end_n=$((start_n + 24999))
db2 "
INSERT INTO biz.sales_order
WITH d(n) AS
(
VALUES 0,1,2,3,4,5,6,7,8,9
),
nums(n) AS
(
SELECT
a.n * 100000 +
b.n * 10000 +
c.n * 1000 +
d1.n * 100 +
e.n * 10 +
f.n + 1
FROM d a, d b, d c, d d1, d e, d f
WHERE
a.n * 100000 +
b.n * 10000 +
c.n * 1000 +
d1.n * 100 +
e.n * 10 +
f.n + 1 BETWEEN ${start_n} AND ${end_n}
)
SELECT
BIGINT(n),
1 + MOD(n, 100000),
DATE('2023-01-01') + MOD(n, 730) DAYS,
CASE MOD(n, 4)
WHEN 0 THEN 'ONLINE'
WHEN 1 THEN 'PARTNER'
WHEN 2 THEN 'DIRECT'
ELSE 'FIELD'
END,
CASE MOD(n, 6)
WHEN 0 THEN 'CREATED'
WHEN 1 THEN 'PAID'
WHEN 2 THEN 'ALLOCATED'
WHEN 3 THEN 'SHIPPED'
WHEN 4 THEN 'CLOSED'
ELSE 'CLOSED'
END,
CASE MOD(n, 6)
WHEN 0 THEN 'PT'
WHEN 1 THEN 'ES'
WHEN 2 THEN 'FR'
WHEN 3 THEN 'DE'
WHEN 4 THEN 'IT'
ELSE 'NL'
END,
CASE MOD(n, 3)
WHEN 0 THEN 'EUR'
WHEN 1 THEN 'EUR'
ELSE 'USD'
END,
CASE MOD(n, 5)
WHEN 0 THEN 'CARD'
WHEN 1 THEN 'CARD'
WHEN 2 THEN 'TRANSFER'
WHEN 3 THEN 'TRANSFER'
ELSE 'INVOICE'
END,
CASE MOD(n, 4)
WHEN 0 THEN 'STANDARD'
WHEN 1 THEN 'STANDARD'
WHEN 2 THEN 'EXPRESS'
ELSE 'PICKUP'
END,
'REP_' || RIGHT(DIGITS(100 + MOD(n, 35)), 3),
CASE
WHEN MOD(n, 5) IN (0,1,2,3) THEN
'L'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| '-P'
|| RIGHT(DIGITS(1000 + MOD(INTEGER((n - 1) / 250), 97)), 4)
WHEN MOD(n, 15) = 0 THEN
'X'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| '-P'
|| RIGHT(DIGITS(1000 + MOD(INTEGER((n - 1) / 250), 53)), 4)
ELSE NULL
END,
CASE
WHEN MOD(n, 6) IN (0,1,2,3) THEN 'APR' || RIGHT(DIGITS(100000000 + MOD(n, 900000000)), 9)
ELSE NULL
END,
CASE
WHEN MOD(n, 6) <> 5 THEN
'LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' wrap pack LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' insert note LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' carrier tag LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
ELSE NULL
END,
CASE
WHEN MOD(n, 10) = 0 THEN
'LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' route bundle LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' route bundle LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' route bundle LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' route bundle priority desk hold'
WHEN MOD(n, 14) = 0 THEN
'LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' split stage LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' split stage LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' split stage LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' split stage late dock'
ELSE
'LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' pack wave LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' pack wave LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' pack wave LOC'
|| RIGHT(DIGITS(100000 + INTEGER((n - 1) / 250)), 6)
|| ' pack wave regular dock'
END,
CASE
WHEN MOD(n, 50) = 0 THEN 90
WHEN MOD(n, 10) = 0 THEN 30
ELSE 0
END,
DECIMAL(20 + MOD(n, 8000) + (MOD(n, 100) / 100.0), 13, 2),
TIMESTAMP('2024-01-01-09.00.00') + MOD(n, 365) DAYS + MOD(n, 86400) SECONDS
FROM nums nsrc
WHERE NOT EXISTS
(
SELECT 1
FROM biz.sales_order so
WHERE so.order_id = BIGINT(nsrc.n)
)"
db2 commit
done
db2 connect reset6.3 Carregar sales_order_line
Este bloco de código gera duas linhas por encomenda, o que produzirá 1000000 de linhas com repetição útil para compressão de dados e de índices. Também aqui usamos lotes com COMMIT, e o filtro NOT EXISTS evita duplicados caso o bloco seja repetido. Neste caso usamos blocos de 25000 encomendas de cada vez.
Tal como em sales_order, introduzimos padrões longos que se repetem fortemente por micro-lotes locais de 250 encomendas, mas que mudam no micro-lote seguinte.
db2 connect to COMPLAB
for start_id in 1 25001 50001 75001 100001 125001 150001 175001 200001 225001 250001 275001 300001 325001 350001 375001 400001 425001 450001 475001
do
end_id=$((start_id + 24999))
db2 "
INSERT INTO biz.sales_order_line
SELECT
BIGINT(o.order_id * 10 + l.line_no),
o.order_id,
l.line_no,
'SKU-' || RIGHT(DIGITS(100000 + MOD(o.order_id + l.line_no, 2500)), 6),
CASE MOD(o.order_id + l.line_no, 6)
WHEN 0 THEN 'LAPTOP'
WHEN 1 THEN 'SERVER'
WHEN 2 THEN 'STORAGE'
WHEN 3 THEN 'NETWORK'
WHEN 4 THEN 'SOFTWARE'
ELSE 'SERVICE'
END,
CASE MOD(o.order_id + l.line_no, 4)
WHEN 0 THEN 'LISB'
WHEN 1 THEN 'MADR'
WHEN 2 THEN 'PARI'
ELSE 'FRAN'
END,
CASE MOD(o.order_id + l.line_no, 3)
WHEN 0 THEN 'NOR'
WHEN 1 THEN 'RED'
ELSE 'RED'
END,
SMALLINT(1 + MOD(o.order_id + l.line_no, 5)),
DECIMAL(15 + MOD(o.order_id + l.line_no, 900) + (MOD(o.order_id, 100) / 100.0), 11, 2),
DECIMAL(CASE WHEN MOD(o.order_id + l.line_no, 8) = 0 THEN 10.00 ELSE 0.00 END, 5, 2),
CASE
WHEN MOD(o.order_id + l.line_no, 20) = 0 THEN
'SN'
|| RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
|| '-'
|| RIGHT(DIGITS(100000 + MOD(o.order_id, 900000)), 6)
ELSE NULL
END,
CASE
WHEN MOD(o.order_id + l.line_no, 9) = 0 THEN
'LOC'
|| RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
|| ' bundle step LOC'
|| RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
|| ' bundle step LOC'
|| RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
|| ' bundle step'
ELSE
'LOC'
|| RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
|| ' pick step LOC'
|| RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
|| ' pick step LOC'
|| RIGHT(DIGITS(100000 + INTEGER((o.order_id - 1) / 250)), 6)
|| ' pick step'
END,
CASE WHEN MOD(o.order_id + l.line_no, 17) = 0 THEN 1 ELSE 0 END,
CASE WHEN MOD(o.order_id + l.line_no, 13) = 0 THEN 0 ELSE 1 END
FROM biz.sales_order o,
(VALUES 1, 2) AS l(line_no)
WHERE o.order_id BETWEEN ${start_id} AND ${end_id}
AND NOT EXISTS
(
SELECT 1
FROM biz.sales_order_line sol
WHERE sol.line_id = BIGINT(o.order_id * 10 + l.line_no)
)"
db2 commit
done
db2 connect reset6.4 Carregamento da tabela pequena e da tabela VOLATILE
cat > /tmp/complab_load_auxiliary.sql <<'SQL'
CONNECT TO COMPLAB;
INSERT INTO biz.small_reference
SELECT *
FROM
(
VALUES
('PT01', 'COUNTRY', 'Portugal'),
('ES01', 'COUNTRY', 'Spain'),
('FR01', 'COUNTRY', 'France'),
('DE01', 'COUNTRY', 'Germany'),
('IT01', 'COUNTRY', 'Italy'),
('NL01', 'COUNTRY', 'Netherlands'),
('ONL1', 'CHANNEL', 'Online direct channel'),
('PAR1', 'CHANNEL', 'Partner resale channel'),
('DIR1', 'CHANNEL', 'Direct sales channel'),
('FLD1', 'CHANNEL', 'Field account channel')
) AS src(ref_code, ref_group, ref_description)
WHERE NOT EXISTS
(
SELECT 1
FROM biz.small_reference r
WHERE r.ref_code = src.ref_code
);
INSERT INTO biz.session_work_queue
WITH d(n) AS
(
VALUES 0,1,2,3,4,5,6,7,8,9
),
nums(n) AS
(
SELECT
a.n * 1000 +
b.n * 100 +
c.n * 10 +
d1.n + 1
FROM d a, d b, d c, d d1
WHERE
a.n * 1000 +
b.n * 100 +
c.n * 10 +
d1.n + 1 <= 5000
)
SELECT
BIGINT(n),
'TOK' || RIGHT(DIGITS(100000000 + MOD(n, 900000000)), 8) || RIGHT(DIGITS(100000000 + MOD(n * 7, 900000000)), 8) || RIGHT(DIGITS(1000 + MOD(n, 9000)), 5),
'WORKER_' || RIGHT(DIGITS(100 + MOD(n, 20)), 3),
CASE MOD(n, 4)
WHEN 0 THEN 'ALLOC'
WHEN 1 THEN 'PICK'
WHEN 2 THEN 'PACK'
ELSE 'SHIP'
END,
CASE
WHEN MOD(n, 5) = 0 THEN 'ORD-' || VARCHAR(100000 + MOD(n, 500000))
ELSE NULL
END,
CASE MOD(n, 3)
WHEN 0 THEN 'N'
WHEN 1 THEN 'R'
ELSE 'D'
END,
TIMESTAMP('2026-01-01-00.00.00') + MOD(n, 30) DAYS + MOD(n, 86400) SECONDS
FROM nums
WHERE NOT EXISTS
(
SELECT 1
FROM biz.session_work_queue q
WHERE q.queue_id = BIGINT(nums.n)
);
COMMIT;
CONNECT RESET;
SQL
db2 -tvf /tmp/complab_load_auxiliary.sql7. Recolha de estatísticas e estabelecimento de baseline
Antes de estimar compressão, atualizamos as estatísticas:
cat > /tmp/complab_runstats_before.sql <<'SQL'
CONNECT TO COMPLAB;
RUNSTATS ON TABLE biz.customer_master WITH DISTRIBUTION AND DETAILED INDEXES ALL;
RUNSTATS ON TABLE biz.sales_order WITH DISTRIBUTION AND DETAILED INDEXES ALL;
RUNSTATS ON TABLE biz.sales_order_line WITH DISTRIBUTION AND DETAILED INDEXES ALL;
RUNSTATS ON TABLE biz.small_reference WITH DISTRIBUTION AND DETAILED INDEXES ALL;
RUNSTATS ON TABLE biz.session_work_queue WITH DISTRIBUTION AND DETAILED INDEXES ALL;
CONNECT RESET;
SQL
db2 -tvf /tmp/complab_runstats_before.sql8. Estimativa de compressão para tabelas sem compressão ativa
Esta é a primeira situação operacional importante: tabelas com organização de dados por linha e ainda sem compressão ativa.
Legenda dos cabeçalhos:
| Cabeçalho | Significado |
|---|---|
tab | Nome da tabela |
npg | NPAGES atual |
card | Cardinalidade estimada da tabela |
psz | Tamanho de página do table space |
mode | Modo atual de row compression |
cur_pct | Percentagem atual de páginas poupadas |
st_pct | Estimativa de poupança com STATIC |
st_row | Tamanho médio estimado da linha com STATIC |
st_pages | Páginas estimadas poupadas com STATIC |
st_mb | MB estimados poupados com STATIC |
ad_pct | Estimativa de poupança com ADAPTIVE |
ad_row | Tamanho médio estimado da linha com ADAPTIVE |
ad_pages | Páginas estimadas poupadas com ADAPTIVE |
ad_mb | MB estimados poupados com ADAPTIVE |
db2 connect to COMPLAB
db2 "
SELECT
CAST(RTRIM(c.tabname) AS VARCHAR(24)) AS tab,
CAST(c.npages AS VARCHAR(8)) AS npg,
CAST(c.card AS VARCHAR(12)) AS card,
CAST(c.pagesize AS VARCHAR(8)) AS psz,
CAST(CASE WHEN ci.rowcompmode IN ('A', 'S') THEN ci.rowcompmode ELSE '-' END AS VARCHAR(4)) AS mode,
CAST(ci.pctpagessaved_current AS VARCHAR(6)) AS cur_pct,
CAST(ci.pctpagessaved_static AS VARCHAR(6)) AS st_pct,
CAST(ci.avgrowsize_static AS VARCHAR(8)) AS st_row,
CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_static / 100.0), 20, 2) AS VARCHAR(12)) AS st_pages,
CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_static / 100.0) * c.pagesize / 1048576.0, 20, 2) AS VARCHAR(10)) AS st_mb,
CAST(ci.pctpagessaved_adaptive AS VARCHAR(6)) AS ad_pct,
CAST(ci.avgrowsize_adaptive AS VARCHAR(8)) AS ad_row,
CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_adaptive / 100.0), 20, 2) AS VARCHAR(12)) AS ad_pages,
CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_adaptive / 100.0) * c.pagesize / 1048576.0, 20, 2) AS VARCHAR(10)) AS ad_mb
FROM
(
SELECT
t.tabschema,
t.tabname,
t.npages,
t.card,
ts.pagesize
FROM syscat.tables t
JOIN syscat.tablespaces ts
ON ts.tbspace = t.tbspace
WHERE t.tabschema = 'BIZ'
AND t.type = 'T'
AND t.tableorg = 'R'
AND t.compression = 'N'
AND t.card > 0
) AS c,
TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(c.tabschema, c.tabname)) AS ci
WHERE ci.object_type = 'DATA'
ORDER BY c.npages DESC"
db2 connect resetInterpretação:
st_pctestima o ganho se ativar classic row compressionad_pctestima o ganho se ativar adaptive compressionst_mbead_mbtraduzem a percentagem em espaço físico potencialmente poupado
9. Estimativa por schema com filtragem prévia
Esta é a forma mais útil quando quer trabalhar por lotes, mas sem avaliar tudo indiscriminadamente. O exemplo abaixo filtra previamente por schema, tamanho mínimo de tabela e exclui tabelas voláteis. Para manter a leitura focada na decisão, a saída mostra apenas as colunas que ajudam a comparar rapidamente STATIC e ADAPTIVE.
db2 connect to COMPLAB
db2 "
WITH candidates AS
(
SELECT
t.tabschema,
t.tabname,
t.npages,
t.card,
ts.pagesize
FROM syscat.tables t
JOIN syscat.tablespaces ts
ON ts.tbspace = t.tbspace
WHERE t.tabschema = 'BIZ'
AND t.type = 'T'
AND t.tableorg = 'R'
AND t.card > 0
AND t.npages > 1000
AND t.compression = 'N'
AND COALESCE(t.volatile, ' ') <> 'C'
)
SELECT
CAST(RTRIM(c.tabname) AS VARCHAR(24)) AS tab,
CAST(c.npages AS VARCHAR(8)) AS npg,
CAST(ci.pctpagessaved_static AS VARCHAR(6)) AS st_pct,
CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_static / 100.0), 20, 2) AS VARCHAR(12)) AS st_pages,
CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_static / 100.0) * c.pagesize / 1048576.0, 20, 2) AS VARCHAR(10)) AS st_mb,
CAST(ci.pctpagessaved_adaptive AS VARCHAR(6)) AS ad_pct,
CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_adaptive / 100.0), 20, 2) AS VARCHAR(12)) AS ad_pages,
CAST(DECIMAL((DECIMAL(c.npages,20,2) * ci.pctpagessaved_adaptive / 100.0) * c.pagesize / 1048576.0, 20, 2) AS VARCHAR(10)) AS ad_mb
FROM candidates c,
TABLE
(
SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(c.tabschema, c.tabname)
) AS ci
WHERE ci.object_type = 'DATA'
ORDER BY
c.npages DESC,
c.card DESC"
db2 connect resetNotas práticas sobre os filtros:
t.tableorg = 'R'impede que tabelas com organização de dados por coluna entrem na análiset.npages > 1000evita perder tempo com objetos demasiado pequenosCOALESCE(t.volatile, ' ') <> 'C'deixa de fora tabelas declaradas como voláteis
Antes de avançar, vale a pena verificar explicitamente o motivo pelo qual cada tabela é incluída ou excluída da análise:
db2 connect to COMPLAB
db2 "
SELECT
CAST(RTRIM(tabname) AS VARCHAR(24)) AS tab,
CAST(npages AS VARCHAR(8)) AS npg,
CAST(COALESCE(volatile, ' ') AS VARCHAR(4)) AS vol,
CAST(compression AS VARCHAR(4)) AS comp,
CASE
WHEN tableorg <> 'R' THEN 'excluded: not row-organized'
WHEN card = 0 THEN 'excluded: no useful statistics'
WHEN COALESCE(volatile, ' ') = 'C' THEN 'excluded: volatile table'
WHEN npages <= 1000 THEN 'excluded: too small'
WHEN compression <> 'N' THEN 'excluded: already compressed'
ELSE 'included in evaluation'
END AS reason
FROM syscat.tables
WHERE tabschema = 'BIZ'
AND type = 'T'
ORDER BY tabname"
db2 connect resetNeste lab deverá observar, em condições normais:
SMALL_REFERENCEexcluída por dimensãoSESSION_WORK_QUEUEexcluída por estar marcada comoVOLATILECUSTOMER_MASTER,SALES_ORDEReSALES_ORDER_LINEincluídas no lote de avaliação
10. Comparar STATIC e ADAPTIVE antes da compressão
Antes de ativar compressão, esta análise permite comparar diretamente STATIC e ADAPTIVE e escolher o modo mais vantajoso para cada tabela.
db2 connect to COMPLAB
db2 "
WITH candidates AS
(
SELECT
t.tabschema,
t.tabname,
t.npages,
t.card,
t.compression,
ts.pagesize
FROM syscat.tables t
JOIN syscat.tablespaces ts
ON ts.tbspace = t.tbspace
WHERE t.tabschema = 'BIZ'
AND t.type = 'T'
AND t.tableorg = 'R'
AND t.card > 0
AND t.npages > 1000
AND t.compression = 'N'
AND COALESCE(t.volatile, ' ') <> 'C'
)
SELECT
CAST(RTRIM(c.tabname) AS VARCHAR(24)) AS tab,
CAST(c.npages AS VARCHAR(8)) AS npg,
CAST(ci.pctpagessaved_static AS VARCHAR(6)) AS st_pct,
CAST(ci.pctpagessaved_adaptive AS VARCHAR(6)) AS ad_pct,
CAST(ci.pctpagessaved_adaptive - ci.pctpagessaved_static AS VARCHAR(6)) AS adv_pct,
CAST(DECIMAL(
(DECIMAL(c.npages,20,2) * (ci.pctpagessaved_adaptive - ci.pctpagessaved_static) / 100.0) * c.pagesize / 1048576.0,
20, 2
) AS VARCHAR(10)) AS extra_mb
FROM candidates c,
TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(c.tabschema, c.tabname)) AS ci
WHERE ci.object_type = 'DATA'
ORDER BY adv_pct DESC, npg DESC"
db2 connect resetLegenda dos cabeçalhos:
| Cabeçalho | Significado |
|---|---|
tab | Nome da tabela |
npg | NPAGES atual |
st_pct | Estimativa com STATIC |
ad_pct | Estimativa com ADAPTIVE |
adv_pct | Diferença ADAPTIVE - STATIC em pontos percentuais |
extra_mb | MB adicionais potencialmente poupados por ADAPTIVE |
11. Descobrir explicitamente onde ADAPTIVE traz vantagem
O resultado da secção anterior serve para escolher a tabela candidata principal. A lógica do lab consiste nos seguintes pontos:
- Se uma tabela grande apresentar um
adv_pctsignificativa, essa tabela é a melhor candidata para compressão adaptativa - Se outra tabela também beneficiar bastante de compressão em termos absolutos, mas tiver uma diferença menor entre
STATICeADAPTIVE, ela continua a ser um bom alvo de compressão, mas pode começar porSTATIC - Depois de ativar a compressão e reorganizar, os dados medidos devem confirmar estas duas ideias: a tabela escolhida para
ADAPTIVEdeverá aproximar-se da estimativa adaptativa, e a tabela deixada emSTATICpoderá ainda mostrar margem adicional potencial na análise final
No cenário pretendido para este lab, ambas as tabelas grandes devem mostrar ganhos fortes com compressão, mas sales_order deverá evidenciar a vantagem mais clara de compressão adaptativa.
12. Estimar ganhos para índices sem compressão
Para índices, o caso de uso clássico é simples: índices sem compressão física, em tabelas grandes, com bastante repetição de chaves ou prefixos.
db2 connect to COMPLAB
db2 "
SELECT
CAST(RTRIM(indname) AS VARCHAR(28)) AS idx,
CAST(RTRIM(tabname) AS VARCHAR(24)) AS tab,
CAST(compress_attr AS VARCHAR(4)) AS attr,
CAST(index_compressed AS VARCHAR(4)) AS icomp,
CAST(pct_pages_saved AS VARCHAR(8)) AS pct_saved,
CAST(num_leaf_pages_saved AS VARCHAR(10)) AS leaf_saved
FROM TABLE
(
SYSPROC.ADMIN_GET_INDEX_COMPRESS_INFO('', 'BIZ', 'SALES_ORDER', NULL, NULL)
) AS t
ORDER BY num_leaf_pages_saved DESC"
db2 connect resetInterpretação:
COMPRESS_ATTR = 'N'eINDEX_COMPRESSED = 'N'significa índice sem compressãoPCT_PAGES_SAVEDestima a percentagem potencial de páginas leaf poupadasNUM_LEAF_PAGES_SAVEDtraduz o ganho para páginas físicas
Legenda dos cabeçalhos:
| Cabeçalho | Significado |
|---|---|
idx | Nome do índice |
tab | Tabela base |
attr | Atributo de compressão definido para o índice |
icomp | Estado atual de compressão do índice |
pct_saved | Percentagem estimada de páginas leaf poupadas |
leaf_saved | Número estimado de páginas leaf poupadas |
Pode repetir a mesma análise para outra tabela:
db2 connect to COMPLAB
db2 "
SELECT
CAST(RTRIM(indname) AS VARCHAR(28)) AS idx,
CAST(RTRIM(tabname) AS VARCHAR(24)) AS tab,
CAST(compress_attr AS VARCHAR(4)) AS attr,
CAST(index_compressed AS VARCHAR(4)) AS icomp,
CAST(pct_pages_saved AS VARCHAR(8)) AS pct_saved,
CAST(num_leaf_pages_saved AS VARCHAR(10)) AS leaf_saved
FROM TABLE
(
SYSPROC.ADMIN_GET_INDEX_COMPRESS_INFO('', 'BIZ', 'SALES_ORDER_LINE', NULL, NULL)
) AS t
ORDER BY num_leaf_pages_saved DESC"
db2 connect reset13. Ativar compressão na tabela e nos índices
Vamos transformar a decisão anterior em ações concretas:
sales_orderserá comprimida comADAPTIVE, porque a estimativa mostra vantagem visível face aSTATICsales_order_lineserá comprimida comSTATIC, não porque comprima pouco, mas porque a diferença inicial face aADAPTIVEé menor e queremos validar depois se ainda existe margem adicional- As colunas com defaults repetidos ativam value compression
- Os índices relevantes passam também a
COMPRESS YES
Antes da alteração, guardamos uma baseline persistente para as duas tabelas. Essa baseline será usada mais à frente para comparar a estimativa prévia com o resultado real medido depois do REORG:
db2 connect to COMPLAB
db2 "
MERGE INTO biz.tab_comp_baseline AS b
USING
(
SELECT
t.tabname,
t.npages,
ts.pagesize,
ci.pctpagessaved_static AS st_pct,
ci.pctpagessaved_adaptive AS ad_pct,
DECIMAL((DECIMAL(t.npages,20,2) * ci.pctpagessaved_static / 100.0) * ts.pagesize / 1048576.0, 20, 2) AS st_mb,
DECIMAL((DECIMAL(t.npages,20,2) * ci.pctpagessaved_adaptive / 100.0) * ts.pagesize / 1048576.0, 20, 2) AS ad_mb
FROM syscat.tables t
JOIN syscat.tablespaces ts
ON ts.tbspace = t.tbspace,
TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(t.tabschema, t.tabname)) AS ci
WHERE t.tabschema = 'BIZ'
AND t.tabname IN ('SALES_ORDER', 'SALES_ORDER_LINE')
AND ci.object_type = 'DATA'
) AS src
ON b.tabname = src.tabname
WHEN MATCHED THEN
UPDATE SET
pre_npages = src.npages,
pagesize = src.pagesize,
st_pct = src.st_pct,
ad_pct = src.ad_pct,
st_mb = src.st_mb,
ad_mb = src.ad_mb
WHEN NOT MATCHED THEN
INSERT (tabname, pre_npages, pagesize, st_pct, ad_pct, st_mb, ad_mb)
VALUES (src.tabname, src.npages, src.pagesize, src.st_pct, src.ad_pct, src.st_mb, src.ad_mb)";
db2 "
SELECT
CAST(RTRIM(tabname) AS VARCHAR(24)) AS tab,
CAST(pre_npages AS VARCHAR(8)) AS pre_npg,
CAST(st_pct AS VARCHAR(6)) AS st_pct,
CAST(ad_pct AS VARCHAR(6)) AS ad_pct,
CAST(st_mb AS VARCHAR(10)) AS st_mb,
CAST(ad_mb AS VARCHAR(10)) AS ad_mb
FROM biz.tab_comp_baseline
WHERE tabname IN ('SALES_ORDER', 'SALES_ORDER_LINE')
ORDER BY tabname"
db2 connect resetLegenda dos cabeçalhos:
| Cabeçalho | Significado |
|---|---|
tab | Nome da tabela |
pre_npg | NPAGES antes da compressão |
st_pct | Estimativa com STATIC antes da compressão |
ad_pct | Estimativa com ADAPTIVE antes da compressão |
st_mb | MB estimados poupados com STATIC antes da compressão |
ad_mb | MB estimados poupados com ADAPTIVE antes da compressão |
Vamos modificar as tabelas de forma a ativar a compressão.
cat > /tmp/complab_enable_compression.sql <<'SQL'
CONNECT TO COMPLAB;
ALTER TABLE biz.sales_order
ALTER fraud_score COMPRESS SYSTEM DEFAULT
COMPRESS YES ADAPTIVE
ACTIVATE VALUE COMPRESSION;
ALTER TABLE biz.sales_order_line
ALTER discount_pct COMPRESS SYSTEM DEFAULT
ALTER backorder_flag COMPRESS SYSTEM DEFAULT
ALTER fulfilled_flag COMPRESS SYSTEM DEFAULT
COMPRESS YES STATIC
ACTIVATE VALUE COMPRESSION;
ALTER INDEX biz.ix_order_customer_date COMPRESS YES;
ALTER INDEX biz.ix_order_status_country COMPRESS YES;
ALTER INDEX biz.ix_order_salesrep COMPRESS YES;
ALTER INDEX biz.ix_line_order COMPRESS YES;
ALTER INDEX biz.ix_line_sku COMPRESS YES;
ALTER INDEX biz.ix_line_wh_family COMPRESS YES;
COMMIT;
CONNECT RESET;
SQL
db2 -tvf /tmp/complab_enable_compression.sql14. Materializar dos dados comprimidos
Ativar as cláusulas de compressão não chega por si só para reformatar imediatamente todos os dados antigos. Para aplicar a nova organização física, temos de reorganizar.
cat > /tmp/complab_reorg.sql <<'SQL'
CONNECT TO COMPLAB;
REORG TABLE biz.sales_order RESETDICTIONARY;
REORG INDEXES ALL FOR TABLE biz.sales_order ALLOW WRITE ACCESS;
REORG TABLE biz.sales_order_line RESETDICTIONARY;
REORG INDEXES ALL FOR TABLE biz.sales_order_line ALLOW WRITE ACCESS;
RUNSTATS ON TABLE biz.sales_order WITH DISTRIBUTION AND DETAILED INDEXES ALL;
RUNSTATS ON TABLE biz.sales_order_line WITH DISTRIBUTION AND DETAILED INDEXES ALL;
CONNECT RESET;
SQL
db2 -tvf /tmp/complab_reorg.sql15. Verificar as melhorias reais, com foco em SALES_ORDER
Primeiro confirmamos se a tabela escolhida passou efetivamente a compressão adaptativa e comparamos a poupança atual com a baseline guardada:
db2 connect to COMPLAB
db2 "
SELECT
CAST(RTRIM(t.tabname) AS VARCHAR(24)) AS tab,
CAST(b.pre_npages AS VARCHAR(8)) AS pre_npg,
CAST(t.npages AS VARCHAR(8)) AS cur_npg,
CAST(CASE WHEN ci.rowcompmode IN ('A', 'S') THEN ci.rowcompmode ELSE '-' END AS VARCHAR(4)) AS mode,
CAST(b.st_pct AS VARCHAR(6)) AS pre_st,
CAST(b.ad_pct AS VARCHAR(6)) AS pre_ad,
CAST(ci.pctpagessaved_current AS VARCHAR(6)) AS cur_pct,
CAST(b.ad_mb AS VARCHAR(10)) AS pre_ad_mb,
CAST(DECIMAL
(
CASE
WHEN ci.pctpagessaved_current BETWEEN 0 AND 99
THEN ((DECIMAL(t.npages,20,4) * 100.0) / (100.0 - ci.pctpagessaved_current) - DECIMAL(t.npages,20,4)) * ts.pagesize / 1048576.0
ELSE NULL
END,
20, 2
) AS VARCHAR(10)) AS cur_mb
FROM syscat.tables t
JOIN syscat.tablespaces ts
ON ts.tbspace = t.tbspace
JOIN biz.tab_comp_baseline b
ON b.tabname = t.tabname,
TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(t.tabschema, t.tabname)) AS ci
WHERE t.tabschema = 'BIZ'
AND t.tabname = 'SALES_ORDER'
AND ci.object_type = 'DATA'"
db2 connect resetO ponto a verificar aqui é simples:
ROWCOMPMODEdeve aparecer comoA.PCTPAGESSAVED_CURRENTdeve aproximar-se da baselinepre_ad.cur_mbdeve aproximar-se depre_ad_mb, dentro da variação normal causada por páginas parcialmente preenchidas e pelo estado real do objeto.
Legenda dos cabeçalhos:
| Cabeçalho | Significado |
|---|---|
tab | Nome da tabela |
pre_npg | NPAGES antes da compressão |
cur_npg | NPAGES depois da compressão |
mode | Modo atual de row compression |
pre_st | Baseline STATIC antes da compressão |
pre_ad | Baseline ADAPTIVE antes da compressão |
cur_pct | Poupança atual medida |
pre_ad_mb | MB estimados com ADAPTIVE antes da compressão |
cur_mb | MB atualmente poupados |
Só depois disso faz sentido olhar para o conjunto completo.
16. Validar o resultado real depois da compressão
Primeiro confirmamos os atributos das tabelas:
db2 connect to COMPLAB
db2 "
SELECT
CAST(RTRIM(t.tabname) AS VARCHAR(24)) AS tab,
CAST(t.compression AS VARCHAR(4)) AS comp,
CAST(t.tableorg AS VARCHAR(4)) AS org,
CAST(CASE WHEN ci.rowcompmode IN ('A', 'S') THEN ci.rowcompmode ELSE '-' END AS VARCHAR(4)) AS mode,
CAST(ci.pctpagessaved_current AS VARCHAR(6)) AS cur_pct
FROM syscat.tables t,
TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(t.tabschema, t.tabname)) AS ci
WHERE t.tabschema = 'BIZ'
AND t.tabname IN ('SALES_ORDER', 'SALES_ORDER_LINE')
AND ci.object_type = 'DATA'"
db2 connect resetLegenda dos cabeçalhos:
| Cabeçalho | Significado |
|---|---|
tab | Nome da tabela |
comp | Atributo de compressão no catálogo |
org | Organização da tabela |
mode | Modo atual de row compression |
cur_pct | Poupança atual medida |
Depois comparamos o ganho atual versus as estimativas guardadas na baseline:
db2 connect to COMPLAB
db2 "
SELECT
CAST(RTRIM(t.tabname) AS VARCHAR(24)) AS tab,
CAST(b.pre_npages AS VARCHAR(8)) AS pre_npg,
CAST(t.npages AS VARCHAR(8)) AS cur_npg,
CAST(CASE WHEN ci.rowcompmode IN ('A', 'S') THEN ci.rowcompmode ELSE '-' END AS VARCHAR(4)) AS mode,
CAST(b.st_pct AS VARCHAR(6)) AS pre_st,
CAST(b.ad_pct AS VARCHAR(6)) AS pre_ad,
CAST(ci.pctpagessaved_current AS VARCHAR(6)) AS cur_pct,
CAST(b.st_mb AS VARCHAR(10)) AS pre_st_mb,
CAST(b.ad_mb AS VARCHAR(10)) AS pre_ad_mb,
CAST(DECIMAL
(
CASE
WHEN ci.pctpagessaved_current BETWEEN 0 AND 99
THEN ((DECIMAL(t.npages,20,4) * 100.0) / (100.0 - ci.pctpagessaved_current) - DECIMAL(t.npages,20,4)) * ts.pagesize / 1048576.0
ELSE NULL
END,
20, 2
) AS VARCHAR(10)) AS cur_mb
FROM syscat.tables t
JOIN syscat.tablespaces ts
ON ts.tbspace = t.tbspace
JOIN biz.tab_comp_baseline b
ON b.tabname = t.tabname,
TABLE(SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(t.tabschema, t.tabname)) AS ci
WHERE t.tabschema = 'BIZ'
AND t.tabname IN ('SALES_ORDER', 'SALES_ORDER_LINE')
AND ci.object_type = 'DATA'"
db2 connect resetAqui a validação é importante:
sales_orderdeve mostrarmode = 'A'e umcur_pctque se aproxime mais da baselinepre_addo que depre_stsales_order_linedeve mostrarmode = 'S'e umcur_pctpróximo da baselinepre_st- Se
sales_order_linetiver ainda uma distância visível entrecur_pctepre_ad, isso prepara exatamente a análise da secção seguinte: existe compressão útil já aplicada, mas ainda pode haver margem adicional comADAPTIVE
E por fim validamos a compressão dos índices:
db2 connect to COMPLAB
db2 "
SELECT
CAST(RTRIM(indname) AS VARCHAR(28)) AS idx,
CAST(RTRIM(tabname) AS VARCHAR(24)) AS tab,
CAST(compress_attr AS VARCHAR(4)) AS attr,
CAST(index_compressed AS VARCHAR(4)) AS icomp,
CAST(pct_pages_saved AS VARCHAR(8)) AS pct_saved,
CAST(num_leaf_pages_saved AS VARCHAR(10)) AS leaf_saved
FROM TABLE
(
SYSPROC.ADMIN_GET_INDEX_COMPRESS_INFO('', 'BIZ', '', NULL, NULL)
) AS t
WHERE tabschema = 'BIZ'
ORDER BY tabname, indname"
db2 connect resetLegenda dos cabeçalhos:
| Cabeçalho | Significado |
|---|---|
idx | Nome do índice |
tab | Tabela base |
attr | Atributo de compressão configurado |
icomp | Estado atual de compressão |
pct_saved | Percentagem de páginas leaf poupadas |
leaf_saved | Número de páginas leaf poupadas |
17. Verificar ganhos adicionais depois da compressão
Numa tabela já comprimida, existe margem adicional se o modo atual não for o melhor, ou se o ganho atual ainda estiver longe da estimativa adaptativa? Observando a tabela sales_order_line, verificamos que a compressão estática trouxe um ganho forte, mas que ainda pode existir algum benefício adicional se adotarmos a compressão adaptativa.
db2 connect to COMPLAB
db2 "
WITH candidates AS
(
SELECT
t.tabschema,
t.tabname,
t.npages,
t.card,
t.compression,
ts.pagesize
FROM syscat.tables t
JOIN syscat.tablespaces ts
ON ts.tbspace = t.tbspace
WHERE t.tabschema = 'BIZ'
AND t.type = 'T'
AND t.tableorg = 'R'
AND t.card > 0
AND t.compression <> 'N'
)
SELECT
CAST(RTRIM(c.tabname) AS VARCHAR(24)) AS tab,
CAST(c.compression AS VARCHAR(4)) AS comp,
CAST(CASE WHEN ci.rowcompmode IN ('A', 'S') THEN ci.rowcompmode ELSE '-' END AS VARCHAR(4)) AS mode,
CAST(ci.pctpagessaved_current AS VARCHAR(6)) AS cur_pct,
CAST(ci.pctpagessaved_static AS VARCHAR(6)) AS st_pct,
CAST(ci.pctpagessaved_adaptive AS VARCHAR(6)) AS ad_pct,
CAST(DECIMAL
(
CASE
WHEN ci.pctpagessaved_current BETWEEN 0 AND 99
THEN (DECIMAL(c.npages,20,4) * 100.0) / (100.0 - ci.pctpagessaved_current)
ELSE NULL
END,
20, 2
) AS VARCHAR(12)) AS unc_pages,
CAST(DECIMAL
(
CASE
WHEN ci.pctpagessaved_current BETWEEN 0 AND 99
THEN
(
((DECIMAL(c.npages,20,4) * 100.0) / (100.0 - ci.pctpagessaved_current))
* (ci.pctpagessaved_adaptive - ci.pctpagessaved_current) / 100.0
* c.pagesize
) / 1048576.0
ELSE NULL
END,
20, 2
) AS VARCHAR(10)) AS extra_mb
FROM candidates c,
TABLE
(
SYSPROC.ADMIN_GET_TAB_COMPRESS_INFO(c.tabschema, c.tabname)
) AS ci
WHERE ci.object_type = 'DATA'
ORDER BY c.npages DESC"
db2 connect resetLeitura do resultado:
- Se
cur_pctjá estiver próximo dead_pct, o espaço adicional recuperável será pequeno. - Se
mode = 'S'ead_pctfor claramente superior, essa é uma situação concreta para considerar compressão adaptativa.
Legenda dos cabeçalhos:
| Cabeçalho | Significado |
|---|---|
tab | Nome da tabela |
comp | Atributo atual de compressão no catálogo |
mode | Modo atual de row compression (S, A ou -) |
cur_pct | Poupança atual medida |
st_pct | Estimativa com STATIC |
ad_pct | Estimativa com ADAPTIVE |
unc_pages | Número inferido de páginas sem compressão |
extra_mb | Ganho adicional estimado em MB se passar para ADAPTIVE |
18. Resumo
Este lab mostrou vários pontos operacionais importantes:
- Compressão de tabelas com dados organizados por linha no Db2 não recorre a uma única funcionalidade. Envolve compressão clássica estática, compressão adaptativa e compressão de valores.
ADMIN_GET_TAB_COMPRESS_INFOeADMIN_GET_INDEX_COMPRESS_INFOpermitem estimar os ganhos com compressão.- Nem todas as tabelas devem entrar na análise. Neste lab uma foi excluída por ser demasiado pequena e outra por estar marcada como
VOLATILE
Uma sequência adequada costuma ser:
1. Filtrar candidatos com base em tamanho, volatilidade e padrão de uso 2. Estimar ganhos para tabelas e índices 3. Ativar compressão apenas nos objetos com retorno claro 4. Reorganizar e recolher estatísticas 5. Medir de novo
19. Cleanup
Quando terminar o lab, remova a base de dados e os ficheiros temporários:
db2 force applications all
db2 drop database COMPLAB
rm -f /tmp/complab_schema.sql \
/tmp/complab_load_customers.sql \
/tmp/complab_load_orders.sql \
/tmp/complab_load_lines.sql \
/tmp/complab_load_auxiliary.sql \
/tmp/complab_runstats_before.sql \
/tmp/complab_enable_compression.sql \
/tmp/complab_reorg.sqlDocumentação IBM útil
Para aprofundar, as referências IBM mais úteis para este tema são: