Todos os produtos
Search
Central de documentação

PolarDB:Use o IMCI para acelerar atualizações de materialized views

Última atualização: Jul 16, 2026

Ao processar grandes volumes de dados (por exemplo, na escala de bilhões de linhas), a atualização de uma materialized view do PostgreSQL torna-se extremamente lenta. Dados desatualizados prejudicam a eficiência de análises de BI e relatórios. O recurso In-Memory Column Index (IMCI) do PolarDB for PostgreSQL reduz significativamente o tempo de atualização da materialized view , melhorando a atualidade dos dados e agilizando análises e relatórios de BI.

Visão geral da solução

O IMCI é o mecanismo de aceleração analítica fornecido pelo PolarDB for PostgreSQL. Ele cria um índice columnstore sobre uma tabela row-store e mantém automaticamente os dados row-store e o índice columnstore sincronizados. Ao executar agregações complexas ou consultas com join, o banco de dados calcula o resultado usando o índice columnstore, oferecendo um desempenho muito superior ao de uma varredura tradicional em row-store.

A ideia central desta solução é criar um índice columnstore nas tabelas base que sustentam uma materialized view, de modo que tanto a criação inicial quanto as atualizações subsequentes da view sejam aceleradas.

image

Pré-requisitos

  • Versões do cluster:

    • PostgreSQL 14 (versão secundária do mecanismo 2.0.14.10.20.0 ou posterior)

    • PostgreSQL 15 (versão secundária do mecanismo 2.0.15.15.7.0 ou posterior)

    • PostgreSQL 16 (versão secundária do mecanismo 2.0.16.8.3.0 ou posterior)

    • PostgreSQL 17 (versão secundária do mecanismo 2.0.17.7.5.0 ou posterior)

    Nota

    Você pode visualizar a versão secundária do mecanismo no console ou executando a instrução SHOW polardb_version;. Se a versão secundária do mecanismo não atender aos requisitos, atualize a versão secundária do mecanismo.

  • Defina o parâmetro wal_level como logical. Essa configuração adiciona as informações necessárias para decodificação lógica ao write-ahead logging (WAL).

    Nota

    É possível definir o parâmetro wal_level no console. A modificação desse parâmetro reinicia o cluster. Planeje suas operações comerciais adequadamente e proceda com cautela.

  • A tabela source deve ter uma chave primária, e a coluna da chave primária deve ser incluída ao criar o índice columnstore. Recomenda-se usar o tipo de dados SERIAL ou BIGSERIAL para a chave primária, pois isso melhora significativamente a eficiência da sincronização de dados.

  • Crie apenas um índice columnstore por tabela.

Observações de uso

  • Cada tabela pode ter apenas um índice columnstore.

  • Índices columnstore não podem ser modificados. Para adicionar colunas a um índice columnstore, recrie o índice.

Preparação

Prepare o ambiente

  1. Um cluster PolarDB for PostgreSQL elegível.

  2. Ative o recurso de índice columnstore.

    O método para ativar o IMCI varia conforme a versão secundária do mecanismo do seu cluster PolarDB for PostgreSQL:

    PostgreSQL 16 (2.0.16.9.8.0 ou posterior) ou PostgreSQL 14 (2.0.14.17.35.0 ou posterior)

    Para clusters PolarDB for PostgreSQL com essas versões, dois métodos estão disponíveis. A tabela a seguir destaca as diferenças.

    Item de comparação

    [Recomendado] Adicionar um nó somente leitura IMCI

    Usar diretamente a extensão de índice columnstore pré-instalada

    Método

    Adicione manualmente um nó somente leitura IMCI no console.

    Nenhuma ação é necessária. Use a extensão diretamente.

    Alocação de recursos

    O mecanismo columnstore usa exclusivamente os recursos do nó, incluindo toda a memória disponível.

    O mecanismo columnstore fica limitado a 25% da memória do nó. A memória restante é alocada para o mecanismo row store.

    Impacto nos negócios

    As cargas de trabalho de processamento transacional (TP) e processamento analítico (AP) ficam isoladas em nós diferentes e não se afetam mutuamente.

    As cargas de trabalho TP e AP são executadas no mesmo nó e podem afetar umas às outras.

    Custos

    Nós somente leitura IMCI geram cobranças adicionais e são faturados à mesma taxa dos nós de computação regulares.

    Sem custo adicional.

    Adicionar um nó somente leitura IMCI

    Existem duas maneiras de adicionar um nó somente leitura IMCI:

    Nota

    O cluster deve conter pelo menos um nó somente leitura. Não é possível adicionar um nó somente leitura IMCI a um cluster de nó único.

    Console

    1. Faça login no console do PolarDB e selecione a região do cluster. Abra o assistente Add/Remove Node de uma das seguintes formas:

      • Na página Clusters, clique em Add/Remove Node na coluna Actions.

      • Na página Basic Information do cluster desejado, clique em Add/Remove Node na seção Database Nodes.

    2. Selecione Add Read-only IMCI Node e clique em OK.

    3. Na página de upgrade/downgrade do cluster, adicione o nó somente leitura IMCI e conclua o pagamento.

      1. Clique em Add a Read-only IMCI Node e selecione as especificações do nó.

      2. Escolha um horário para o switchover.

      3. (Opcional) Revise os Termos de Serviço do Produto e o Acordo de Nível de Serviço.

      4. Clique em Buy Now.

    4. Após a conclusão do pagamento, retorne à página de detalhes do cluster e aguarde a adição do nó somente leitura IMCI. O nó estará pronto quando seu status mudar para Running.

    During purchase

    Na página de compra do PolarDB, na seção Nodes, especifique o número de IMCI Read-Only Nodes.

    PostgreSQL 16 (2.0.16.8.3.0 a 2.0.16.9.8.0) ou PostgreSQL 14 (2.0.14.10.20.0 a 2.0.14.17.35.0)

    Em clusters PolarDB for PostgreSQL com essas versões, o recurso IMCI é fornecido como a extensão polar_csi. Para usar o IMCI, crie primeiro a extensão no banco de dados desejado.

    Nota
    • A extensão polar_csi tem escopo no nível do banco de dados. Para usar o IMCI em vários bancos de dados dentro de um cluster, crie a extensão polar_csi para cada banco de dados.

    • A conta de banco de dados usada para instalar a extensão deve ser uma conta privilegiada.

    Há duas maneiras de instalar a extensão polar_csi:

    Console

    1. Faça login no console do PolarDB. No painel de navegação à esquerda, clique em Clusters. Selecione a região onde seu cluster está localizado e clique no ID do cluster para abrir a página de detalhes do cluster.

    2. No painel de navegação à esquerda, escolha Settings and Management > Extension Management. Na aba Extension Management, selecione Uninstalled Extensions.

    3. No canto superior direito da página, selecione o banco de dados alvo. Na linha da extensão polar_csi, clique em Install na coluna Actions. Na caixa de diálogo Install Extension, selecione a Database Account alvo e clique em OK para instalar a extensão no banco de dados alvo.

    CLI

    Conecte-se ao cluster de banco de dados e execute a seguinte instrução em um banco de dados onde você tenha permissões suficientes para criar a extensão polar_csi.

    CREATE EXTENSION polar_csi;
  3. No banco de dados alvo (banco de dados de negócios), instale a extensão pg_hint_plan. Essa extensão permite usar hints especiais no estilo de comentários para ajustar planos de consulta já escolhidos.

    CREATE EXTENSION pg_hint_plan;
  4. No banco de dados de sistema postgres, instale a extensão pg_cron (tarefas agendadas). Essa extensão executa tarefas automaticamente em um horário ou intervalo especificado.

    1. Mude para o banco de dados.

      \c postgres;
    2. Instale a extensão.

      CREATE EXTENSION pg_cron;

Prepare os dados

No banco de dados alvo (banco de dados de negócios), crie a tabela customers e a tabela orders, crie índices columnstore nelas e insira dados de teste.

  1. Mude para o banco de dados alvo (banco de dados de negócios). Este exemplo usa testdb.

    \c testdb;
  2. Crie as tabelas e insira os dados.

    -- Create the customers table and its columnstore index.
    CREATE TABLE customers (
        customer_id SERIAL PRIMARY KEY,
        customer_name VARCHAR(100),
        email VARCHAR(100)
    );
    CREATE INDEX idx_customers_csi ON customers USING csi;
    
    -- Create the orders table and its columnstore index.
    CREATE TABLE orders (
        order_id SERIAL PRIMARY KEY,
        order_date DATE,
        amount DECIMAL(10, 2),
        customer_id INT REFERENCES customers(customer_id)
    );
    CREATE INDEX idx_orders_csi ON orders USING csi;
    
    -- Insert data into the customers table.
    INSERT INTO customers (customer_name, email) VALUES
    ('Alice', 'alice@example.com'),
    ('Bob', 'bob@example.com'),
    ('Charlie', 'charlie@example.com');
    
    -- Insert data into the orders table.
    INSERT INTO orders (order_date, amount, customer_id) VALUES
    ('2025-06-01', 200.00, 1),
    ('2025-06-02', 150.00, 2),
    ('2025-06-03', 300.00, 1),
    ('2025-06-04', 100.00, 3);

Crie a materialized view

Ao criar a materialized view, use hints para forçar o otimizador de consultas a calcular a view por meio do índice columnstore.

/*+ SET(polar_csi.enable_query on) SET(polar_csi.cost_threshold 0) SET(polar_csi.max_parallel_workers 6) SET(polar_csi.memory_limit 10240) */CREATE MATERIALIZED VIEW mv_customer_orders AS
SELECT
    c.customer_name AS customer_name,
    o.order_date AS order_date,
    o.amount AS amount
FROM
    orders o
JOIN
    customers c ON o.customer_id = c.customer_id;

Parâmetros de hint

Parâmetro

Descrição

polar_csi.enable_query on

Permite que a consulta use o índice columnstore.

polar_csi.cost_threshold 0

Força o otimizador a usar o índice columnstore definindo o limiar de custo como 0.

polar_csi.max_parallel_workers 6

Define o grau de paralelismo para computação columnstore. Recomenda-se um valor que não exceda o número de núcleos de CPU no nó.

polar_csi.memory_limit 10240

Define a memória disponível para computação, em MB.

Nota

O parâmetro polar_csi.max_parallel_workers era anteriormente chamado polar_csi.exec_parallel em versões anteriores do kernel. Para versões do kernel que não suportam polar_csi.max_parallel_workers, use polar_csi.exec_parallel em seu lugar.

  • PostgreSQL 14:

    • Use polar_csi.exec_parallel nas versões 2.0.14.20.42.0 e anteriores.

    • Use polar_csi.max_parallel_workers nas versões 2.0.14.20.43.0 e posteriores.

  • PostgreSQL 16:

    • Use polar_csi.exec_parallel nas versões 2.0.16.11.15.0 e anteriores.

    • Use polar_csi.max_parallel_workers nas versões 2.0.16.13.16.0 e posteriores.

Atualize a materialized view

Crie a função de atualização

O processo de atualização é encapsulado em uma função. Recomendamos a função a seguir porque ela substitui a nova view com segurança, preservando seus índices e propriedade.

Nota

A função a seguir é fornecida como referência. Ela garante uma substituição segura da view, mas você ainda deve testá-la minuciosamente em seu próprio ambiente antes de usá-la em produção.

-- view_name is the name of the materialized view. schema_name is the schema in which the view resides (defaults to current_schema). new_owner is the owner assigned to the newly created view.
CREATE OR REPLACE FUNCTION refresh_materialized_view_safely_using_csi(
    view_name TEXT,
    schema_name TEXT DEFAULT NULL,
    new_owner TEXT DEFAULT NULL
)
RETURNS BOOL
LANGUAGE plpgsql
AS $$
DECLARE
    view_definition TEXT;
    new_view_name TEXT;
    old_view_name TEXT;
    index_record RECORD;
    index_creation_sql TEXT;
    explain_result TEXT;
    target_schema TEXT;
    qualified_old_name TEXT;
    qualified_new_name TEXT;
    current_owner TEXT;
    grant_record RECORD;
BEGIN
    -- Determine the target schema (use the input parameter or the current schema).
    IF schema_name IS NULL THEN
        target_schema := current_schema();
    ELSE
        target_schema := schema_name;
    END IF;

    -- Construct fully qualified table names.
    qualified_old_name := format('%I.%I', target_schema, view_name);
    qualified_new_name := format('%I.%I', target_schema, view_name || '_new');

    RAISE NOTICE 'Operating in schema: %', target_schema;

    -- Verify that the materialized view exists.
    IF NOT EXISTS (
        SELECT 1 FROM pg_matviews
        WHERE matviewname = view_name
        AND schemaname = target_schema
    ) THEN
        RAISE EXCEPTION 'Materialized view "%" does not exist in schema "%"', view_name, target_schema;
    END IF;

    -- Retrieve the definition and current owner of the materialized view.
    SELECT m.definition, p.rolname INTO view_definition, current_owner
    FROM pg_matviews m
    JOIN pg_class c ON m.matviewname = c.relname AND m.schemaname = target_schema
    JOIN pg_roles p ON c.relowner = p.oid
    WHERE m.matviewname = view_name
    AND m.schemaname = target_schema;

    IF view_definition IS NULL THEN
        RAISE EXCEPTION 'Failed to retrieve definition for materialized view "%"', view_name;
    END IF;

    -- Set the names for the old and new views.
    old_view_name := view_name;
    new_view_name := view_name || '_new';

    -- IMCI performance parameters.
    SET LOCAL polar_csi.cost_threshold = 0;

    -- Print the query plan.
    RAISE NOTICE 'Query plan for materialized view refresh:';
    FOR explain_result IN EXECUTE format('/*+ SET(polar_csi.enable_query on) */ EXPLAIN CREATE MATERIALIZED VIEW %s AS %s', qualified_new_name, view_definition) LOOP
        RAISE NOTICE '%', explain_result;
    END LOOP;

    BEGIN
        -- Create the new materialized view.
        EXECUTE format('/*+ SET(polar_csi.enable_query on) */ CREATE MATERIALIZED VIEW %s AS %s', qualified_new_name, view_definition);

        -- If a new owner is specified, set the owner.
        IF new_owner IS NOT NULL THEN
            -- Verify that the user exists.
            IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = new_owner) THEN
                RAISE EXCEPTION 'Role "%" does not exist', new_owner;
            END IF;

            EXECUTE format('ALTER MATERIALIZED VIEW %s OWNER TO %I', qualified_new_name, new_owner);
            RAISE NOTICE 'Changed owner from "%" to "%"', current_owner, new_owner;
        END IF;

        -- Copy all indexes from the old view to the new view.
        FOR index_record IN
            SELECT indexname, indexdef
            FROM pg_indexes
            WHERE tablename = old_view_name
            AND schemaname = target_schema
        LOOP
            -- Replace the old view name with the new view name.
            index_creation_sql := regexp_replace(
                index_record.indexdef,
                ' ON ' || target_schema || '.' || old_view_name || ' ',
                ' ON ' || target_schema || '.' || new_view_name || ' ',
                'i'
            );

            -- Handle the special case of UNIQUE indexes.
            index_creation_sql := regexp_replace(
                index_creation_sql,
                'INDEX ' || index_record.indexname || ' ON',
                'INDEX ' || index_record.indexname || '_new ON',
                'i'
            );

            RAISE NOTICE 'Creating index: %', index_creation_sql;
            EXECUTE index_creation_sql;
        END LOOP;

        -- Copy view permissions.
        RAISE NOTICE 'Restoring permissions to new view %.%', target_schema, new_view_name;
        FOR grant_record IN
        SELECT
            (acl).grantee::regrole::text AS grantee,
            (acl).privilege_type
        FROM
            pg_class c
        JOIN pg_namespace n ON c.relnamespace = n.oid
        CROSS JOIN aclexplode(c.relacl) AS acl
        WHERE
            n.nspname = target_schema
        AND c.relname = old_view_name
        LOOP
            CONTINUE WHEN grant_record.grantee IS NULL;
            -- RAISE NOTICE 'Granting % ON %.% TO %',
            --     grant_record.privilege_type, target_schema, new_view_name, grant_record.grantee;

            EXECUTE format(
                'GRANT %s ON %I.%I TO %s',
                grant_record.privilege_type,
                target_schema,
                new_view_name,
                quote_ident(grant_record.grantee)
            );
        END LOOP;

        -- Drop the old materialized view.
        EXECUTE format('DROP MATERIALIZED VIEW %s', qualified_old_name);

        -- Rename the new materialized view to the original name.
        EXECUTE format('ALTER MATERIALIZED VIEW %s RENAME TO %I', qualified_new_name, old_view_name);

        -- Rename the indexes (remove the _new suffix).
        FOR index_record IN
            SELECT indexname
            FROM pg_indexes
            WHERE tablename = old_view_name
            AND schemaname = target_schema
        LOOP
            IF position('_new' in index_record.indexname) > 0 THEN
                EXECUTE format(
                    'ALTER INDEX %I.%I RENAME TO %I',
                    target_schema,
                    index_record.indexname,
                    replace(index_record.indexname, '_new', '')
                );
            END IF;
        END LOOP;

        RETURN TRUE;
    EXCEPTION
        WHEN OTHERS THEN
            RAISE EXCEPTION 'Failed to refresh materialized view: %', SQLERRM;
            RETURN FALSE;
    END;
END;
$$;

Parâmetros

Parâmetro

Descrição

refresh_materialized_view_safely_using_csi

Nome da função. Pode ser alterado para atender às necessidades do seu negócio.

view_name

Nome da materialized view.

schema_name

Schema onde a materialized view reside. O padrão é current_schema.

new_owner

Novo proprietário da materialized view após sua recriação.

polar_csi.enable_query on

Permite que a consulta use o índice columnstore.

polar_csi.cost_threshold 0

Força o otimizador a usar o índice columnstore definindo o limiar de custo como 0.

polar_csi.max_parallel_workers 6

Define o grau de paralelismo para computação columnstore. Recomenda-se um valor que não exceda o número de núcleos de CPU no nó.

polar_csi.memory_limit 10240

Define a memória disponível para computação, em MB.

Nota

O parâmetro polar_csi.max_parallel_workers era anteriormente chamado polar_csi.exec_parallel em versões anteriores do kernel. Para versões do kernel que não suportam polar_csi.max_parallel_workers, use polar_csi.exec_parallel em seu lugar.

  • PostgreSQL 14:

    • Use polar_csi.exec_parallel nas versões 2.0.14.20.42.0 e anteriores.

    • Use polar_csi.max_parallel_workers nas versões 2.0.14.20.43.0 e posteriores.

  • PostgreSQL 16:

    • Use polar_csi.exec_parallel nas versões 2.0.16.11.15.0 e anteriores.

    • Use polar_csi.max_parallel_workers nas versões 2.0.16.13.16.0 e posteriores.

  • Execute a atualização

    Refresh manually

    Quando seu negócio exigir, chame a função manualmente para acionar uma atualização. Substitua o nome na chamada pelo nome real da sua materialized view. Este exemplo usa mv_customer_orders.

    SELECT refresh_materialized_view_safely_using_csi('mv_customer_orders');

    Schedule refreshes with pg_cron

    Nota
    • Tarefas só podem ser criadas no banco de dados de sistema postgres, e apenas uma conta privilegiada pode criá-las.

    • Especifique o proprietário da materialized view recriada para que usuários regulares ainda possam ler a view após sua recriação por uma conta privilegiada. Caso precise ajustar outras configurações relacionadas a permissões, modifique a função de atualização definida acima.

    • Ao usar o pg_cron para agendar atualizações, certifique-se de que o intervalo da tarefa seja estritamente maior que a duração real da atualização; caso contrário, as tarefas se acumularão. Como uma atualização grava dados, ela geralmente é muito mais lenta do que um simples SELECT.

    Create a scheduled task

    Mude para o banco de dados de sistema postgres e use o pg_cron para especificar o nome da tarefa, o intervalo e a operação. Para mais informações, consulte a extensão pg_cron (tarefas agendadas).

    1. Mude para o banco de dados.

      \c postgres;
    2. Crie a tarefa agendada. Substitua os parâmetros relevantes pelos seus valores reais.

      Nota
      • Substitua <mv_name> pelo nome real da materialized view.

      • Substitua <database_name> pelo nome real do banco de dados de negócios.

      • Substitua <schema_name> pelo nome real do schema.

      • Substitua <user_name> pelo nome de usuário real.

      Sintaxe

      SELECT cron.schedule_in_database(
          'refresh_mv_customer_orders',  -- Task name (customizable)
          '*/5 * * * *',                 -- Cron expression, for example, run every 5 minutes
          $$SELECT refresh_materialized_view_safely_using_csi('<mv_name>', '<schema_name>', '<user_name>')$$,
          '<database_name>'
      );

      Exemplo

      SELECT cron.schedule_in_database(
          'refresh_mv_customer_orders',  -- Task name (customizable)
          '*/5 * * * *',                 -- Cron expression, for example, run every 5 minutes
          $$SELECT refresh_materialized_view_safely_using_csi('mv_customer_orders', 'public', 'polarpg')$$,
          'testdb'
      );

    View scheduled tasks

    Execute a seguinte instrução SQL para visualizar as tarefas agendadas configuradas.

    SELECT * FROM cron.job;

    Saída esperada:

    jobid  |  schedule   |                                           command                                            | nodename | nodeport | database | username | active |          jobname
    -------+-------------+----------------------------------------------------------------------------------------------+----------+----------+----------+----------+--------+----------------------------
         1 | */5 * * * * | SELECT refresh_materialized_view_safely_using_csi('mv_customer_orders', 'public', 'polarpg') | /data/.  |     3000 | testdb   | polarpg  | t      | refresh_mv_customer_orders
    (1 row)

    Delete a scheduled task

    Se não precisar mais de atualizações agendadas, execute a seguinte instrução SQL para excluir a tarefa agendada.

    SELECT cron.unschedule('refresh_my_materialized_view');

    View task execution details

    Execute a seguinte instrução SQL para visualizar os detalhes de execução das tarefas agendadas.

    SELECT * FROM cron.job_run_details;

    Saída esperada:

     jobid | runid | job_pid | database | username |                                           command                                            |  status   | return_message |          start_time           |           end_time
    -------+-------+---------+----------+----------+----------------------------------------------------------------------------------------------+-----------+----------------+-------------------------------+-------------------------------
         1 |     1 |   76537 | testdb   | polarpg  | SELECT refresh_materialized_view_safely_using_csi('mv_customer_orders', 'public', 'polarpg') | succeeded | 1 row          | 2025-08-27 08:35:00.007231+00 | 2025-08-27 08:35:00.024946+00
    (1 rows)

    Consulte a materialized view

    Execute a seguinte instrução SQL para consultar a materialized view. Substitua o nome pelo nome real da sua materialized view. Este exemplo usa mv_customer_orders.

    Nota

    Antes de executar a instrução, mude para o seu banco de dados de negócios real.

    SELECT customer_name, COUNT(*) FROM mv_customer_orders GROUP BY customer_name;