Otimização de performance em Power BI

Otimização de performance em Power BI|Implementação e Consultoria em Business Intelligence - BI|
Pedro Tebaldi Autor: Pedro Tebaldi PM do KeepGreen
Publicado jul/2022Atualizado set/2026

Um relatório que leva oito segundos para abrir raramente tem um culpado óbvio. O atraso pode estar na fórmula da medida, na quantidade de visuais da página ou no tamanho do modelo carregado na memória. Sem separar essas três coisas, o ajuste vira tentativa e erro.

Este guia mostra como medir cada uma delas dentro do Power BI Desktop e o que fazer com o número que aparecer. Todos os valores citados aqui saíram de um laboratório próprio, sobre um modelo de vendas com 1.000.000 de linhas.

Ao final você terá o critério para decidir entre reescrever a medida, podar colunas do modelo ou trocar o modo de armazenamento. Nessa ordem, porque é a ordem do retorno.

 

Onde a lentidão está: consulta DAX, visual ou o resto

No Power BI Desktop, o Performance Analyzer separa o tempo de cada visual em três partes: consulta DAX, exibição do visual e outro. É por ele que você descobre se a lentidão vem da fórmula ou da renderização, antes de mexer em qualquer coisa.

Abaixo está a gravação de um laboratório montado em 1 de setembro de 2026, no Power BI Desktop 2.155.756.0. São dois visuais idênticos sobre a mesma tabela de 1.000.000 de linhas, mudando apenas a medida exibida.

 
Painel Performance Analyzer do Power BI Desktop com a gravação iniciada em 01/09/2026, mostrando o visual Depois: SUM direto na coluna em 68 ms com 5 ms de consulta DAX e o visual Antes: SUMX com CALCULATE linha a linha em 285 ms com 212 ms de consulta DAX

 

Com SUM direto na coluna, o visual levou 68 ms, dos quais 5 ms de consulta DAX. Com SUMX e CALCULATE linha a linha, foram 285 ms, com 212 ms de consulta. Ambos devolvem exatamente o mesmo número na tela.

No entanto, o que decide é a proporção, não o total. Quando a consulta DAX passa de dois terços do tempo, o problema está na medida ou no modelo. Quando “Exibição de visual” e “Outro” dominam, mexa na página: menos visuais, menos linhas por tabela, menos colunas por visual.

Vale abrir a documentação do analisador antes da primeira gravação. A ordem dos passos muda o resultado: limpe a lista, inicie a gravação, depois atualize os visuais.

 

A medida que custa 42 vezes mais para dar a mesma resposta

Ambas as medidas abaixo devolvem 6.463.745. A diferença está em quantas vezes o motor precisa trocar de contexto até chegar lá.




medidas.dax
// 285 ms no visual, 212 ms de consulta DAX
Quantidade por venda =
SUMX (
    VALUES ( Vendas[VendaID] ),
    CALCULATE ( SUM ( Vendas[Quantidade] ) )
)

// 68 ms no visual, 5 ms de consulta DAX
Quantidade total =
SUM ( Vendas[Quantidade] )

Na primeira, o motor percorre os 1.000.000 de valores distintos de VendaID e força uma transição de contexto a cada um deles. Já a segunda soma a coluna inteira de uma vez. Esse é o trabalho para o qual o motor colunar foi desenhado.

Fora do visual, o contraste fica ainda maior. Em três execuções com o cache limpo, a mediana da primeira consulta ficou em 245,4 ms contra 4,2 ms da segunda.

Portanto, o sinal de alerta é sempre o mesmo: um iterador como SUMX ou AVERAGEX percorrendo coluna de alta cardinalidade, com CALCULATE dentro. Para entender por que essa combinação cobra caro, vale a leitura sobre contexto de linha e contexto de filtro na linguagem DAX.

Nem toda troca de fórmula paga, no entanto. No mesmo laboratório, substituir FILTER sobre a tabela por KEEPFILTERS no predicado não mudou o tempo: 5,9 ms contra 6,1 ms. Ali a escolha existe por causa do resultado, não do custo.

 

O que ocupa memória não é linha, é cardinalidade

Todas as colunas da tabela de vendas do laboratório têm o mesmo 1.000.000 de linhas. Quanto cada uma cobra de memória, porém, varia em três ordens de grandeza.

 

Coluna Valores distintos KB na memória Do modelo
VendaID 1.000.000 6.510,7 85,1%
ValorLinha coluna calculada 5.819 291,0 3,8%
PrecoUnitario 5.778 289,9 3,8%
Desconto 1.189 116,2 1,5%
Data 1.085 115,1 1,5%
CategoriaRelated coluna calculada 3 17,8 0,2%
Regiao 4 17,0 0,2%
Quantidade 12 5,5 0,1%
ProdutoID 8 2,4 0,0%

 

Sozinha, a coluna VendaID responde por 6.510,7 KB dos 7,47 MB do modelo. Ela é uma chave técnica: nenhum visual a exibe, nenhum filtro a usa e nenhuma relação depende dela. Enquanto isso, Data cabe em 115,1 KB com as mesmas 1.000.000 de linhas, porque tem apenas 1.085 valores distintos.

Daí a regra que rende mais rápido: antes de reescrever fórmula, remova as colunas que nenhum visual, relação ou medida usa. Nesse modelo de teste, quatro colunas nessa condição somavam 90,7% do total.

Esses números saíram da consulta abaixo, executada na exibição de consulta DAX do próprio Power BI Desktop:

 
Resultado de consulta DAX no Power BI Desktop com a memória por coluna da tabela Vendas, em que VendaID ocupa 6.510,7 KB contra 17 KB da coluna Regiao e 5,5 KB da coluna Quantidade

 




memoria-por-coluna.dax
// Troque "Vendas" pelo nome da sua tabela.
EVALUATE
VAR Segmentos =
    SELECTCOLUMNS (
        FILTER ( INFO.STORAGETABLECOLUMNSEGMENTS (), [DIMENSION_NAME] = "Vendas" ),
        "Coluna",
            VAR t = [TABLE_ID]
            VAR b = IF ( LEFT ( t, 2 ) = "H$", MID ( t, FIND ( "$", t, 3, 0 ) + 1, 100 ), [COLUMN_ID] )
            RETURN IF ( FIND ( " (", b, 1, 0 ) > 0, LEFT ( b, FIND ( " (", b, 1, 0 ) - 1 ), b ),
        "Bytes", [USED_SIZE] + 0
    )
VAR Dicionarios =
    SELECTCOLUMNS (
        FILTER ( INFO.STORAGETABLECOLUMNS (), [DIMENSION_NAME] = "Vendas" && LEFT ( [TABLE_ID], 2 ) <> "H$" ),
        "Coluna",
            VAR b = [COLUMN_ID]
            RETURN IF ( FIND ( " (", b, 1, 0 ) > 0, LEFT ( b, FIND ( " (", b, 1, 0 ) - 1 ), b ),
        "Bytes", [DICTIONARY_SIZE] + 0
    )
VAR Tudo = UNION ( Segmentos, Dicionarios )
VAR PorColuna = GROUPBY ( Tudo, [Coluna], "@Bytes", SUMX ( CURRENTGROUP (), [Bytes] ) )
RETURN
SELECTCOLUMNS ( PorColuna, "Coluna", [Coluna], "KB na memoria", ROUND ( [@Bytes] / 1024, 1 ) )
ORDER BY [KB na memoria] DESC

Com INFO.STORAGETABLECOLUMNSEGMENTS, o VertiPaq expõe os próprios segmentos sem instalar nada por fora. Some USED_SIZE por coluna, junte o dicionário que vem de INFO.STORAGETABLECOLUMNS e o mapa de memória sai em uma consulta. Linhas com INDEX, POS_TO_ID e RowNumber são estruturas internas do motor.

 

Coluna calculada e data automática: o que cobram sem avisar

No laboratório, a coluna calculada ValorLinha guardava quantidade vezes preço, já resolvido linha a linha. Ela custou 291,0 KB de memória. Enquanto isso, a medida equivalente com SUMX respondeu no mesmo tempo da soma da coluna pronta: 4,7 ms contra 4,6 ms.

Ou seja, essa coluna cobrou memória e não devolveu velocidade nenhuma. As técnicas de redução de dados da Microsoft recomendam o caminho inverso. Quando a coluna for mesmo necessária, crie no Power Query: a compactação ali é melhor e o cálculo fica fora da fila de atualização.

Confira também a opção Data/hora automática. O Power BI cria uma tabela de datas oculta para cada coluna de data do modelo. Cada uma delas soma ao tamanho final. Aqui elas eram pequenas, com 1.096 dias de calendário, mas o custo cresce com a amplitude de anos importados.

 

Modelo estrela: por que buscar valor na outra tabela custa

O modelo estrela mantém a tabela de fatos no centro e as dimensões em volta, cada uma ligada por uma relação simples. Com esse desenho, o motor usa a relação declarada para cruzar tabelas em vez de procurar valor a cada linha.

Em números, a diferença aparece. A mesma soma escrita com LOOKUPVALUE levou 8,6 ms; com RELATED, que aproveita a relação existente, levou 4,9 ms. Ainda assim, as duas devolveram 88.164.812.720,00.

Duas armadilhas moram nesse ponto. Relação bidirecional entre fato e dimensão cria caminho ambíguo e cobra em toda consulta. Prefira filtro simples e resolva a exceção com CROSSFILTER dentro da medida. Chave de texto, por sua vez, exige codificação de hash e uma busca a cada consulta, enquanto chave numérica usa codificação de valor.

 

Menos visuais na página, menos consultas por atualização

Cada visual dispara a própria consulta ao abrir o relatório e a cada clique em filtro. Uma página com quinze visuais gera quinze consultas. Depois disso, o navegador ainda precisa desenhar as quinze antes de devolver o controle ao usuário.

No laboratório, esse custo de renderização aparece medido. No visual rápido, a consulta DAX consumiu 5 ms e o item “Outro” consumiu 55 ms. Isto é, quase todo o tempo foi preparação, não cálculo.

Aqui o critério é editorial antes de ser técnico: cada visual precisa responder a uma pergunta que alguém faz de verdade. Quando a página vira painel de tudo, a conta chega na abertura. Compare com exemplos de dashboards montados por área, em que cada tela resolve uma decisão.

 

Import ou DirectQuery: o critério, não a definição

No modo de importação, os dados entram comprimidos na memória e as consultas rodam contra o VertiPaq. A Microsoft estima compactação da ordem de 10 vezes, então 10 GB de origem chegam perto de 1 GB dentro do modelo.

No DirectQuery nada é copiado: cada visual vira consulta na fonte, com a atualidade que a fonte tiver e a latência que ela cobrar. Portanto a escolha é de requisito, não de preferência.

Fique na importação quando a granularidade couber na capacidade e a atualização agendada atender ao negócio. Vá de DirectQuery quando o dado precisa refletir a fonte em segundos ou quando o volume não cabe na memória. Já o modelo composto resolve o meio do caminho: resumo importado nas páginas de abertura, detalhe em DirectQuery na página de detalhamento.

 

Capacidade não conserta modelo: o que mudou com o Fabric

Subir de licença era a última recomendação da versão anterior deste artigo. Ela envelheceu. A Microsoft não vende mais as SKUs P do Power BI Premium: cada assinatura se encerra ao fim do contrato vigente e o substituto é a capacidade do Microsoft Fabric, as SKUs F.

Segundo as perguntas frequentes da migração, o mapeamento sai por unidades de capacidade: P1 vira F64, P2 vira F128, P3 vira F256. A troca não acontece sozinha, porque alguém precisa comprar a capacidade no Azure e reatribuir cada espaço de trabalho. As faixas de licença estão detalhadas no guia de licenciamento do Power BI.

Continua apertando o mesmo limite: a capacidade compartilhada hospeda modelos de até 1 GB. Subir de capacidade para acomodar 6 MB de chave técnica, contudo, é a forma mais cara de chegar ao mesmo resultado. Corte as colunas primeiro, meça de novo, discuta capacidade depois.

 

BI & Analytics

Transformamos dados brutos em decisões estratégicas e insights.

Construímos dashboards interativos, KPIs e relatórios automatizados integrados às suas fontes de dados existentes.

Fale com um Especialista →

 

Por onde começar na segunda-feira

Abra o Performance Analyzer, grave uma atualização de visuais e ordene a lista por duração. O visual mais lento diz onde está o custo. Em seguida, a divisão em três partes diz de que tipo ele é. Esse passo leva dois minutos e evita semanas de ajuste no lugar errado.

Se a consulta DAX domina, procure iterador sobre coluna de alta cardinalidade e reescreva a medida. Se o modelo está grande, rode a consulta de memória por coluna e corte o que nenhum visual usa. Se o tempo aparece espalhado entre muitos visuais, a página é que precisa de poda.

Um relatório de vendas bem desenhado costuma caber em menos colunas do que o time imagina, como mostra este exemplo de relatório de BI. Quer ajuda para medir, podar e reconstruir os modelos que travam a operação? Fale com um especialista da OpServices.

Acompanhe a OpServices10.576 profissionais de TI já seguemSeguir

Estou na OpServices desde 2011, onde sou Gerente de Marketing e Product Manager do KeepGreen, plataforma de gestão de incidentes de TI que higieniza alertas, aponta causa raiz com IA, escreve o post-mortem e analisa custos de nuvem. Também lidero os projetos de governança de inteligência artificial da empresa. Escrevo neste blog desde 2013, com mais de 550 artigos publicados sobre monitoramento, observabilidade, SRE e ITSM. LinkedIn

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *