Pyxis Logo
Início / Formação IBM / Artigo técnico

Lab prático de compressão de tabelas e índices no Db2

Como avaliar, ativar e validar compressão de dados em tabelas com organização por linha e seus índices.

24 min de leitura
Publicado 2026-05-13
Pyxis editorial team
Classifique este artigo
Classificação média: Sem classificação
A sua classificação: Sem classificação
visualizações: 0
Ilustração técnica de compressão de tabelas e índices no Db2

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çãoCláusula típicaUso principal
Classic row compressionCOMPRESS YES STATICDicionário estável, útil quando quer uma compressão previsível após REORG
Adaptive compressionCOMPRESS YES ADAPTIVEDicionário adaptativo, normalmente a opção mais interessante para tabelas OLTP com evolução de dados contínua
Value compressionACTIVATE VALUE COMPRESSIONRemove armazenamento redundante de NULL, strings vazias e valores repetidos em colunas específicas
System default compressionALTER col COMPRESS SYSTEM DEFAULTEvita armazenar explicitamente valores default do sistema, como zeros ou blanks
Index compressionALTER INDEX ... COMPRESS YESReduz 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_code e gift_message
  • Outros têm combinações diferentes de shipping_mode, sales_rep ou approval_code

Este lab não estabelece compressão adaptativa como primeira escolha. Primeiro mede:

  • PCTPAGESSAVED_STATIC
  • PCTPAGESSAVED_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 - db2inst1

Se 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.sql

6. 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 VOLATILE que 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.sql

6.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 reset

6.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 reset

6.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.sql

7. 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.sql

8. 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çalhoSignificado
tabNome da tabela
npgNPAGES atual
cardCardinalidade estimada da tabela
pszTamanho de página do table space
modeModo atual de row compression
cur_pctPercentagem atual de páginas poupadas
st_pctEstimativa de poupança com STATIC
st_rowTamanho médio estimado da linha com STATIC
st_pagesPáginas estimadas poupadas com STATIC
st_mbMB estimados poupados com STATIC
ad_pctEstimativa de poupança com ADAPTIVE
ad_rowTamanho médio estimado da linha com ADAPTIVE
ad_pagesPáginas estimadas poupadas com ADAPTIVE
ad_mbMB 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 reset

Interpretação:

  • st_pct estima o ganho se ativar classic row compression
  • ad_pct estima o ganho se ativar adaptive compression
  • st_mb e ad_mb traduzem 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 reset

Notas práticas sobre os filtros:

  • t.tableorg = 'R' impede que tabelas com organização de dados por coluna entrem na análise
  • t.npages > 1000 evita perder tempo com objetos demasiado pequenos
  • COALESCE(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 reset

Neste lab deverá observar, em condições normais:

  • SMALL_REFERENCE excluída por dimensão
  • SESSION_WORK_QUEUE excluída por estar marcada como VOLATILE
  • CUSTOMER_MASTER, SALES_ORDER e SALES_ORDER_LINE incluí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 reset

Legenda dos cabeçalhos:

CabeçalhoSignificado
tabNome da tabela
npgNPAGES atual
st_pctEstimativa com STATIC
ad_pctEstimativa com ADAPTIVE
adv_pctDiferença ADAPTIVE - STATIC em pontos percentuais
extra_mbMB 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_pct significativa, 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 STATIC e ADAPTIVE, ela continua a ser um bom alvo de compressão, mas pode começar por STATIC
  • Depois de ativar a compressão e reorganizar, os dados medidos devem confirmar estas duas ideias: a tabela escolhida para ADAPTIVE deverá aproximar-se da estimativa adaptativa, e a tabela deixada em STATIC poderá 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 reset

Interpretação:

  • COMPRESS_ATTR = 'N' e INDEX_COMPRESSED = 'N' significa índice sem compressão
  • PCT_PAGES_SAVED estima a percentagem potencial de páginas leaf poupadas
  • NUM_LEAF_PAGES_SAVED traduz o ganho para páginas físicas

Legenda dos cabeçalhos:

CabeçalhoSignificado
idxNome do índice
tabTabela base
attrAtributo de compressão definido para o índice
icompEstado atual de compressão do índice
pct_savedPercentagem estimada de páginas leaf poupadas
leaf_savedNú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 reset

13. Ativar compressão na tabela e nos índices

Vamos transformar a decisão anterior em ações concretas:

  • sales_order será comprimida com ADAPTIVE, porque a estimativa mostra vantagem visível face a STATIC
  • sales_order_line será comprimida com STATIC, não porque comprima pouco, mas porque a diferença inicial face a ADAPTIVE é 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 reset

Legenda dos cabeçalhos:

CabeçalhoSignificado
tabNome da tabela
pre_npgNPAGES antes da compressão
st_pctEstimativa com STATIC antes da compressão
ad_pctEstimativa com ADAPTIVE antes da compressão
st_mbMB estimados poupados com STATIC antes da compressão
ad_mbMB 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.sql

14. 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.sql

15. 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 reset

O ponto a verificar aqui é simples:

  • ROWCOMPMODE deve aparecer como A.
  • PCTPAGESSAVED_CURRENT deve aproximar-se da baseline pre_ad.
  • cur_mb deve aproximar-se de pre_ad_mb, dentro da variação normal causada por páginas parcialmente preenchidas e pelo estado real do objeto.

Legenda dos cabeçalhos:

CabeçalhoSignificado
tabNome da tabela
pre_npgNPAGES antes da compressão
cur_npgNPAGES depois da compressão
modeModo atual de row compression
pre_stBaseline STATIC antes da compressão
pre_adBaseline ADAPTIVE antes da compressão
cur_pctPoupança atual medida
pre_ad_mbMB estimados com ADAPTIVE antes da compressão
cur_mbMB 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 reset

Legenda dos cabeçalhos:

CabeçalhoSignificado
tabNome da tabela
compAtributo de compressão no catálogo
orgOrganização da tabela
modeModo atual de row compression
cur_pctPoupanç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 reset

Aqui a validação é importante:

  • sales_order deve mostrar mode = 'A' e um cur_pct que se aproxime mais da baseline pre_ad do que de pre_st
  • sales_order_line deve mostrar mode = 'S' e um cur_pct próximo da baseline pre_st
  • Se sales_order_line tiver ainda uma distância visível entre cur_pct e pre_ad, isso prepara exatamente a análise da secção seguinte: existe compressão útil já aplicada, mas ainda pode haver margem adicional com ADAPTIVE

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 reset

Legenda dos cabeçalhos:

CabeçalhoSignificado
idxNome do índice
tabTabela base
attrAtributo de compressão configurado
icompEstado atual de compressão
pct_savedPercentagem de páginas leaf poupadas
leaf_savedNú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 reset

Leitura do resultado:

  • Se cur_pct já estiver próximo de ad_pct, o espaço adicional recuperável será pequeno.
  • Se mode = 'S' e ad_pct for claramente superior, essa é uma situação concreta para considerar compressão adaptativa.

Legenda dos cabeçalhos:

CabeçalhoSignificado
tabNome da tabela
compAtributo atual de compressão no catálogo
modeModo atual de row compression (S, A ou -)
cur_pctPoupança atual medida
st_pctEstimativa com STATIC
ad_pctEstimativa com ADAPTIVE
unc_pagesNúmero inferido de páginas sem compressão
extra_mbGanho 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_INFO e ADMIN_GET_INDEX_COMPRESS_INFO permitem 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.sql

Documentação IBM útil

Para aprofundar, as referências IBM mais úteis para este tema são:

Mais nesta área

Mais nesta área

Voltar à formação
Categoria de artigos

Artigos de Db2 LUW

Veja todos os artigos técnicos de Db2 LUW numa única página de categoria.

Abrir categoria Db2 LUW