DEV Community

Mateus Rauli
Mateus Rauli

Posted on

POSTGRESQL - ÍNDICE GIN NA PRÁTICA

Hoje vim relatar minha experiência com o índice GIN, que resultou numa melhora muito positiva na performance de algumas das consultas mais lentas do sistema que atuei.

Introdução ao GIN INDEX

O GIN (Generalized Inverted iNdex) é um índice designado para lidar com tipos de dados que são subdivisíveis, ou seja, podem ser "divididos" em partes menores, permitindo a pesquisa de valores individuais. É muito utilizado para consultas em dados estruturados e não estruturados como array, jsonb e campos de texto para busca full-text.

Introduzido na versão 8.2 do PostgreSQL, rapidamente se tornou uma solução indispensável para cenários onde se precisa de buscas rápidas em dados complexos. Diferente do índice padrão B-TREE que é mais adequado para buscas de valores únicos, o GIN é otimizado para casos onde o mesmo valor pode estar associado a vários registros, assim como os valores de um array.

Como eu utilizei o GIN INDEX

Certo dia, me deparei com um problema crítico no sistema: uma consulta estava causando uma lentidão significativa e prejudicando o desempenho geral da aplicação. Após analisar, percebi que ela fazia uso de uma coluna do tipo ARRAY em seu filtro.

Consultando a documentação oficial do PostgreSQL, encontrei o índice GIN como uma possível solução. Decidi implementá-lo na coluna problemática para avaliar o impacto. O resultado foi impressionante: o tempo de execução da consulta caiu drasticamente e, com isso, o sistema se tornou visivelmente mais ágil.

O impacto foi ainda maior porque a coluna onde apliquei o índice GIN era amplamente utilizada em diversas partes do sistema, tornando essa melhoria um divisor de águas para a performance geral. Essa experiência reforçou minha percepção de por que o GIN é frequentemente chamado de "índice mágico".

Desvantagens da utilização do GIN Index

Como visto até então, o índice traz sim diversas vantagens quando o assunto é lidar com dados complexos, porém nem tudo é perfeito e ele possui desvantagens que devem ser consideradas e analisadas caso você esteja pensando em implementar ele na sua tabela.

Uma grande desvantagem são as operações de escrita que se tornam mais custosas já que o índice deve se atualizar para refletir as mudanças dos dados, ou seja, cada operação que altera o dado exige a atualização do índice também, o que é mais custoso em tabelas que possuem alta frequência de escrita.

Além disso, o GIN consome mais memória do que os outros índices, tanto de armazenamento quanto de consulta, especialmente se a coluna indexada contiver muitos valores únicos ou complexos.

Exemplo de criação do índice

Para poder exemplificar o uso do índice, criei uma tabela chamada users contendo apenas id e data.

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    data JSONB
);
Enter fullscreen mode Exit fullscreen mode

Para criarmos o índice, basta usar o USING indicando qual o tipo de índice que será utilizado

CREATE INDEX idx_data_gin ON users USING gin (data);

Pronto! Índice criado, agora ao analisar a consulta será possível notar que ele utilizara o índice criado para buscar a informação.

Query plan
"Bitmap Heap Scan on users u  (cost=12.00..16.01 rows=1 width=68) (actual time=0.022..0.023 rows=1 loops=1)"
"  Recheck Cond: (data @> '{""name"": ""Mateus""}'::jsonb)"
"  Heap Blocks: exact=1"
"  ->  Bitmap Index Scan on idx_data_gin  (cost=0.00..12.00 rows=1 width=0) (actual time=0.011..0.011 rows=1 loops=1)"
"        Index Cond: (data @> '{""name"": ""Mateus""}'::jsonb)"
"Planning Time: 0.120 ms"
"Execution Time: 0.061 ms"
Enter fullscreen mode Exit fullscreen mode

Note que você sempre verá Bitmap Index Scan no plano de execução pois este é o plano de varredura compatível com o índice GIN. Ou seja, isso não significa que seu índice está sendo usado de maneira ineficiente, apenas que é o comportamento esperado.

GIN Multicoluna, uma alternativa para B-TREE x GIN

Em consultas que envolvem colunas com características distintas, pode ser necessário combinar índices B-TREE e GIN. Uma abordagem inicial seria criar índices separados, com um índice B-TREE para colunas que lidam bem com valores únicos e um índice GIN para colunas com dados mais complexos. Embora essa solução funcione, ela pode não ser eficiente em termos de desempenho, pois cada índice é avaliado separadamente.

Uma alternativa interessante é o uso de índices GIN multicoluna. Com essa abordagem, é possível indexar várias colunas em um único índice, cobrindo diferentes tipos de dados. No entanto, para colunas que não são subdivisíveis (como integer ou timestamp), é necessário habilitar a extensão btree_gin do PostgreSQL. Essa extensão permite que esses tipos sejam indexados de maneira compatível dentro de um índice GIN.

CREATE EXTENSION btree_gin;
CREATE INDEX ON records USING gin (data, customer_id);
Enter fullscreen mode Exit fullscreen mode

Com esses comandos você consegue criar um índice GIN multicoluna.

Embora seja uma solução menos comum, ela pode ser útil em cenários específicos. No entanto, é importante considerar que índices maiores resultam em maior uso de I/O e custos adicionais em operações de escrita, como inserções e atualizações.

Considerações finais

Com grandes poderes, vem grandes responsabilidades.

Embora o GIN seja extremamente eficiente em cenários específicos, ele não é uma solução universal para todos os problemas relacionados à performance de consultas. Em muitos casos, o tradicional B-TREE continua sendo a escolha mais adequada, especialmente para consultas simples ou quando se busca por valores únicos. É essencial avaliar cuidadosamente o problema em questão para determinar se a inclusão de um índice é realmente necessária e, caso seja, qual o tipo de índice que melhor atenderá às necessidades do sistema.

Referências

https://www.postgresql.org/docs/current/gin.html
https://pganalyze.com/blog/gin-index

Top comments (0)