Blog

17 de maio de 2022

Banco de Dados Colunar com o MariaDB — Parte 2

No artigo anterior abordamos um pouco sobre o que são os bancos de dados com armazenamento colunar e como eles organizam os dados para que o acesso seja mais eficiente quando trabalhamos com consultas analíticas, enfatizando a implementação ColumnStore no MariaDB. Nesta segunda parte iremos falar um pouco sobre a sua arquitetura interna e como podemos importar grandes volumes de dados para tabelas com esta engine além de fazer um comparativo de performance com o InnoDB, engine default do MySQL e do MariaDB, que armazena dados no formato tradicional orientado a linhas.

Arquitetura Interna

De maneira resumida, a implementação ColumnStore no MariaDB é composta por dois módulos, o User Module e o Performance Module. Ambos rodam em processos próprios, podendo ser executados na mesma máquina ou em servidores separados. Também podem ter sua execução paralelizada aumentando assim a possibilidade de escalar horizontalmente o banco de dados conforme a demanda da aplicação aumenta.

User Modules

São responsáveis por receber as consultas SQL dos clientes e as quebrar em instruções de baixo nível que são enviadas para execução pelos Performance Modules. Ao receber as respostas das consultas, o User Module que recebeu e tratou a requisição do cliente as unifica e devolve a resposta final.

Dentre as atividades desempenhadas por eles podemos destacar:

Transformar o plano de consulta calculado pelo MariaDB em uma lista de tarefas ColumnStore.

Enviar as instruções de execução de consultas para os Performance Modules;

Executar operações de Join conforme necessário, dependendo se a tabela envolvida for pequena o suficiente.

Receber dados dos Performance Modules;

Executar instruções de pós-processamento para as operações de agregação de dados.

Retornar dados para a interface final MariaDB.


Performance Modules

São os responsáveis por receber as instruções de consulta em baixo nível enviadas pelos User Modules, tratar e processar os dados das tabelas ColumnStore. Fazem o “grosso” do trabalho, buscando no disco ou no buffer em memória as informações solicitadas pelas consultas SQL. Cada novo Performance Module adicionado ao cluster aumenta a capacidade de processamento paralelo do banco.


Importando Dados com o Comando cpimport

A melhor maneira de se importar dados pré-existentes para o ColumnStore no MariaDB é usando o utilitário cpimport. Com ele podemos importar gigabytes de dados de forma bastante rápida, sem a necessidade de executar todo o controle de transações envolvidas em uma operação normal de Insert. Como discutimos no artigo anterior, operações que alteram dados nas tabelas ColumnStore tendem a ser bem mais lentas do que as operações de leitura, pois o armazenamento colunar prioriza a leitura de dados ao invés da inserção e atualização.

O comando cpimport aceita qualquer arquivo em texto plano nos quais cada linha representa um registro que deve ser inserido na tabela criada com a engine ColumnStore. O caracter de separação default é o “|” (pipe), mas podemos indicar qualquer outro separador no momento da execução.

Alguns pontos devem ser considerados sobre o processo de importação de dados:

A sequência de dados em cada linha do arquivo de importação deve estar na mesma sequência das colunas na tabela para onde os dados serão importados;

Valores do tipo data devem estar no formato YYYY-MM-DD, como exemplo 2022–04–20;

A importação com o cpimport apenas adiciona novas linhas à uma tabela existente. Assim dados antigos são preservados;

A maneira mais simples de se executar o comando cpimport é:

cpimport <BancoDeDados> <Tabela> <Arquivo>

Onde:

<BancoDeDados>: representa o nome do banco de dados que contém a tabela destino ColumnStore da importação;

<Tabela>: é o nome da tabela destino da importação;

<Arquivo>: caminho do arquivo que deverá ser importado.

Exemplos de Importação

Importando arquivo separado por vírgulas (CSV):

cpimport centraldb vendas importacao.csv -s ‘,’

Importando arquivo CSV diretamente da entrada padrão:

cat importacao.csv | cpimport centraldb vendas -s ‘,’

Importando dados diretamente de um banco de dados existente, redirecionando a saída da consulta para o cpimport:

mariadb — quick — skip-column-names — execute=”SELECT * FROM vendas” | cpimport -s ‘\t’ centraldb vendas

Base de Dados de Exemplo

Vamos usar um arquivo CSV chamado “vendas_export.csv” que contém dados gerados de forma aleatória apenas para ilustrar a utilização do comando cpimport e também fazer os testes de comparativo de performance entre tabelas ColumnStore e InnoDB (armazenamento tradicional orientado a linhas).

O arquivo contém 100 milhões de registros com tamanho aproximado de 12 GB. Ele representa uma lista de produtos adquiridos por clientes em uma loja onde cada linha do arquivo contém os dados do cliente, os dados do produto e também dados sobre a compra em si. Iremos fazer a importação em uma única tabela.

A tabela de destino deverá ser criada com os seguintes campos:


Iremos criar um novo banco de dados chamado “datacorp” e nele criar a nova tabela com o nome “vendas” utilizando a engine ColumnStore.

Com a tabela criada podemos agora importar o conteúdo do arquivo CSV. Devemos sair do shell do MariaDB e executar o comando cpimport diretamente no terminal. Para facilitar a chamada do comando iremos executá-lo na mesma pasta em que o nosso arquivo CSV foi gravado. Lembre-se de usar a instrução “sudo” antes do comando para que o cpimport tenha permissão suficiente para escrever nos arquivos de armazenamento de colunas da tabela recém criada:

sudo cpimport datacorp vendas vendas_export.csv -s ‘,’

Caso ocorra algum erro durante a importação, será possível conhecer a causa analisando os arquivos gerados na pasta /var/log/mariadb/columnstore/cpimport, mais especificamente os arquivos que tiverem a extensão “.bad” e “.err”. Neles conseguimos saber exatamente quais linhas provocaram o erro e o motivo de eles terem ocorrido.

Levará algum tempo para a importação finalizar sendo que o tempo total dependerá muito da máquina que você utilizar. Para executar os exemplos deste artigo foi utilizado um computador desktop com processador Core i5 12400 de 6 núcleos, 16 GB de memória RAM e SSD NVMe PCIe 4 com 512 GB de armazenamento. Com estas configurações, o tempo de carregamento foi de 59 segundos para o nosso arquivo de 12 GB.

Executando o comando SELECT COUNT(*) na tabela “vendas” podemos ver que todos os 10 milhões de registros foram importados.

Teste de Performance ColumnStore X InnoDB

Vamos agora comparar a performance entre tabelas ColumnStore e InnoDB. Para isso iremos criar um novo banco de dados com o nome “datacorp_innodb” e uma nova tabela com o mesmo nome e campos da tabela criada com a engine ColumnStore. Basta omitir a instrução “engine=ColumnStore” do comando CREATE TABLE que a nova tabela seja criada utilizando a engine padrão InnoDB.

Podemos utilizar a consulta “select table_schema, table_name, engine from information_schema.tables where table_name = ‘vendas’;” para nos certificar que as duas tabelas foram criadas usando engines diferentes.

A melhor maneira de importarmos os dados do nosso arquivo CSV também para a tabela InnoDB é utilizando a instrução LOAD DATA LOCAL INFILE. Assim, no terminal iremos permanecer na pasta que contém o nosso arquivo de importação, abrir o shell do MariaDB e executar o seguinte comando:

Desta vez para importar os 10 milhões de registros o tempo gasto foi de 5 minutos e 56 segundos.

Comparando Consultas

Para fazer o comparativo de performance entre a tabela de vendas com a engine ColumnStore e a tabela vendas com a engine InnoDB, iremos executar 3 vezes cada consulta e calcular a média de tempo entre elas. Repare que neste primeiro momento não criamos índices na tabela vendas_innodb justamente para calcular a diferença de tempo de uma operação de Scan nas tabelas ColumnStore e InnoDB. Num cenário da “vida real” é comum precisarmos gerar um novo relatório que precisa filtrar ou agrupar registros nos quais as colunas utilizadas como filtros não estão indexadas. Esse é o primeiro grande benefício de tabelas ColumnStore, não precisar de índices para filtrar os dados de forma eficiente.

SELECT COUNT(*) FROM vendas


SELECT COUNT(*) FROM vendas WHERE produto_id = 83

SELECT * FROM (SELECT cidade, uf, COUNT(*) AS qtde_vendas FROM vendas GROUP BY cidade, uf) AS ranking ORDER BY qtde_vendas DESC LIMIT 5

Considerações Finais

Como podemos perceber, o tempo de processamento das consultas na tabela ColumnStore é muito menor do que o tempo gasto para executar as mesmas consultas na tabela InnoDB, na qual o armazenamento é orientado à linhas. Os dados na tabela ColumnStore já são gravados de uma forma que fique eficiente a busca e carregamento das informações. Isso traz um benefício gigantesco quando chega aquela demanda para gerar um relatório de última hora que fatalmente vai acessar alguma coluna que não estaria indexada no armazenamento tradicional em linhas.

Poderíamos explorar ainda neste artigo várias possibilidades de se melhorar o desempenho da tabela vendas com engine InnoDB, como por exemplo a criação de índices. O problema é que cada novo índice criado para otimizar as consultas traz um custo adicional de espaço extra de armazenamento, tempo extra para inserção de dados e ainda não é garantido que o índice atenderá todas as futuras consultas que surgirão durante a evolução de uma aplicação.

Mas é claro que nem tudo são flores. ColumnStore foi criado pensando na velocidade de leitura e processamento de grandes volumes de dados em consultas analíticas. Não é recomendável utilizá-lo em operações que envolvem controle de transações, como o gerenciamento de estoque em um e-commerce, reserva de assentos em aplicações de compra de passagens e etc. Nesses casos, é recomendado utilizar o armazenamento orientado a linhas como da própria engine InnoDB, pois nele cada registro é armazenado em um mesmo arquivo e por consequência a velocidade de escrita acaba sendo superior ao do ColumnStore, que armazena cada coluna de um mesmo registro em arquivos separados.

O assunto é bastante extenso e poderíamos escrever um livro inteiro explorando em detalhes todas as funcionalidades do ColumnStore, fazendo mais comparativos com o InnoDB, otimizando servidores para diversos tipos de caso de uso e etc. Mas espero que esses dois artigos sobre o ColumnStore tenham despertado sua curiosidade sobre o assunto, principalmente em explorar tipos de bancos de dados diferentes para necessidades e aplicações diferentes. Como diz o ditado, “para quem só sabe usar martelo, todo problema é um prego” 😅.


Links Úteis

Para saber mais sobre o ColumnStore:

https://mariadb.com/kb/en/mariadb-columnstore/

Imagem Docker com a engine ColumnStore ativada no MariaDB e pronta para uso: https://hub.docker.com/r/mariadb/columnstore