A maioria dos bancos de dados cresce em tamanho ao longo do tempo. O crescimento nem sempre é rápido o suficiente para impactar o desempenho do banco de dados, mas definitivamente há casos em que isso acontece. Quando isso acontece, muitas vezes nos perguntamos o que poderia ser feito para reduzir esse impacto e como podemos garantir operações suaves do banco de dados ao lidar com dados em grande escala.
Antes de mais nada, vamos tentar definir o que significa um “grande volume de dados”? Para MySQL ou MariaDB é InnoDB descompactado. O InnoDB funciona de uma maneira que se beneficia fortemente da memória disponível - principalmente do buffer pool do InnoDB. Contanto que os dados caibam lá, o acesso ao disco é minimizado para lidar apenas com gravações - as leituras são servidas fora da memória. O que acontece quando os dados superam a memória? Mais e mais dados precisam ser lidos do disco quando há necessidade de acessar linhas, que não estão armazenadas em cache no momento. Quando a quantidade de dados aumenta, a carga de trabalho muda de limite de CPU para limite de E/S. Isso significa que o gargalo não é mais a CPU (que era o caso quando os dados cabem na memória - o acesso aos dados na memória é rápido, a transformação e a agregação de dados são mais lentas), mas sim o subsistema de E/S (as operações da CPU nos dados são muito mais rápido do que acessar dados do disco.) Com o aumento da adoção do flash, as cargas de trabalho vinculadas à E/S não são tão terríveis quanto costumavam ser nos tempos das unidades giratórias (o acesso aleatório é muito mais rápido com o SSD), mas o impacto no desempenho ainda está lá .
Outra coisa que devemos ter em mente é que normalmente nos preocupamos apenas com o conjunto de dados ativo. Claro, você pode ter terabytes de dados em seu esquema, mas se você precisar acessar apenas os últimos 5 GB, essa é realmente uma situação muito boa. Claro, ainda apresenta desafios operacionais, mas em termos de desempenho ainda deve estar ok.
Vamos apenas supor para os propósitos deste blog, e esta não é uma definição científica, que por grande volume de dados queremos dizer casos em que o tamanho dos dados ativos supera significativamente o tamanho da memória. Pode ser 100 GB quando você tem 2 GB de memória, pode ser 20 TB quando você tem 200 GB de memória. O ponto crítico é que sua carga de trabalho é estritamente limitada por E/S. Tenha paciência conosco enquanto discutimos algumas das opções disponíveis para MySQL e MariaDB.
Particionamento
A abordagem histórica (mas perfeitamente válida) para lidar com grandes volumes de dados é implementar o particionamento. A ideia por trás disso é dividir a tabela em partições, uma espécie de sub-tabelas. A divisão acontece de acordo com as regras definidas pelo usuário. Vamos dar uma olhada em alguns dos exemplos (os exemplos SQL são retirados da documentação do MySQL 8.0)
O MySQL 8.0 vem com os seguintes tipos de particionamento:
- RANGE
- LISTA
- COLUNAS
- HASH
- CHAVE
Também pode criar subpartições. Não vamos reescrever a documentação aqui, mas ainda gostaríamos de lhe dar algumas dicas sobre como as partições funcionam. Para criar partições, você precisa definir a chave de particionamento. Pode ser uma coluna ou no caso de RANGE ou LIST várias colunas que serão usadas para definir como os dados devem ser divididos em partições.
O particionamento HASH requer que o usuário defina uma coluna, que será hash. Em seguida, os dados serão divididos em um número de partições definido pelo usuário com base nesse valor de hash:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
)
PARTITION BY HASH( YEAR(hired) )
PARTITIONS 4;
Neste caso o hash será criado com base no resultado gerado pela função YEAR() na coluna ‘hired’.
O particionamento KEY é semelhante com a exceção de que o usuário define qual coluna deve ser hash e o resto depende do MySQL para manipular.
Enquanto as partições HASH e KEY distribuem dados aleatoriamente pelo número de partições, RANGE e LIST permitem que o usuário decida o que fazer. RANGE é comumente usado com hora ou data:
CREATE TABLE quarterly_report_status (
report_id INT NOT NULL,
report_status VARCHAR(20) NOT NULL,
report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),
PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),
PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),
PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),
PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),
PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),
PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),
PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),
PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),
PARTITION p9 VALUES LESS THAN (MAXVALUE)
);
Também pode ser usado com outro tipo de colunas:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT NOT NULL,
store_id INT NOT NULL
)
PARTITION BY RANGE (store_id) (
PARTITION p0 VALUES LESS THAN (6),
PARTITION p1 VALUES LESS THAN (11),
PARTITION p2 VALUES LESS THAN (16),
PARTITION p3 VALUES LESS THAN MAXVALUE
);
As partições LIST funcionam com base em uma lista de valores que classifica as linhas em várias partições:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
)
PARTITION BY LIST(store_id) (
PARTITION pNorth VALUES IN (3,5,6,9,17),
PARTITION pEast VALUES IN (1,2,10,11,19,20),
PARTITION pWest VALUES IN (4,12,13,14,18),
PARTITION pCentral VALUES IN (7,8,15,16)
);
Qual é o ponto em usar partições, você pode perguntar? O ponto principal é que as pesquisas são significativamente mais rápidas do que com a tabela não particionada. Digamos que você queira pesquisar as linhas que foram criadas em um determinado mês. Se você tiver vários anos de dados armazenados na tabela, isso será um desafio - um índice terá que ser usado e, como sabemos, os índices ajudam a encontrar linhas, mas acessar essas linhas resultará em várias leituras aleatórias de toda a mesa. Se você tiver partições criadas ano-mês, o MySQL pode apenas ler todas as linhas dessa partição em particular - não há necessidade de acessar o índice, não há necessidade de fazer leituras aleatórias:basta ler todos os dados da partição, sequencialmente, e estamos tudo pronto.
As partições também são muito úteis para lidar com a rotação de dados. Se o MySQL puder identificar facilmente linhas para excluir e mapeá-las para uma única partição, em vez de executar DELETE FROM table WHERE …, que usará o índice para localizar linhas, você poderá truncar a partição. Isso é extremamente útil com o particionamento RANGE - seguindo o exemplo acima, se quisermos manter os dados por apenas 2 anos, podemos facilmente criar um cron job, que removerá a partição antiga e criará uma nova e vazia para o próximo mês.
Compressão InnoDB
Se temos um grande volume de dados (não necessariamente pensando em banco de dados), a primeira coisa que nos vem à mente é comprimi-lo. Existem inúmeras ferramentas que oferecem a opção de compactar seus arquivos, reduzindo significativamente seu tamanho. O InnoDB também tem uma opção para isso - tanto MySQL quanto MariaDB suportam compressão InnoDB. A principal vantagem do uso da compressão é a redução da atividade de E/S. Os dados, quando compactados, são menores, portanto, são mais rápidos de ler e escrever. A página típica do InnoDB tem 16 KB de tamanho, para SSD são 4 operações de E/S para ler ou gravar (SSD normalmente usa páginas de 4 KB). Se conseguirmos compactar 16 KB em 4 KB, reduzimos as operações de E/S em quatro. Isso realmente não ajuda muito em relação à relação entre o conjunto de dados e a memória. Na verdade, pode até piorar - o MySQL, para operar nos dados, precisa descompactar a página. No entanto, ele lê a página compactada do disco. Isso resulta no buffer pool do InnoDB armazenando 4 KB de dados compactados e 16 KB de dados não compactados. É claro que existem algoritmos para remover dados desnecessários (páginas não compactadas serão removidas quando possível, mantendo apenas uma compactada na memória), mas você não pode esperar muitas melhorias nessa área.
Também é importante ter em mente como funciona a compactação em relação ao armazenamento. As unidades de estado sólido são a norma para servidores de banco de dados atualmente e possuem algumas características específicas. Eles são rápidos, não se importam muito se o tráfego é sequencial ou aleatório (mesmo que ainda prefiram o acesso sequencial ao aleatório). Eles são caros para grandes volumes. Eles sofrem de “desgaste”, pois podem lidar com um número limitado de ciclos de gravação. A compactação ajuda significativamente aqui - reduzindo o tamanho dos dados no disco, reduzimos o custo da camada de armazenamento do banco de dados. Ao reduzir o tamanho dos dados que gravamos no disco, aumentamos a vida útil do SSD.
Infelizmente, mesmo que a compactação ajude, para volumes maiores de dados ainda pode não ser suficiente. Outro passo seria procurar algo diferente do InnoDB.
MyRocks
MyRocks é um mecanismo de armazenamento disponível para MySQL e MariaDB que é baseado em um conceito diferente do InnoDB. Meu colega Sebastian Insausti tem um blog legal sobre como usar MyRocks com MariaDB. A essência é que, devido ao seu design (ele usa Log Structured Merge, LSM), o MyRocks é significativamente melhor em termos de compactação do que o InnoDB (que é baseado na estrutura B+Tree). MyRocks foi projetado para lidar com grandes quantidades de dados e reduzir o número de gravações. Originou-se do Facebook, onde os volumes de dados são grandes e os requisitos para acessar os dados são altos. Assim, o armazenamento SSD - ainda assim, em uma escala tão grande, todo ganho na compactação é enorme. O MyRocks pode oferecer compactação até 2x melhor do que o InnoDB (o que significa que você reduz o número de servidores em dois). Ele também foi projetado para reduzir a amplificação de gravação (número de gravações necessárias para lidar com uma alteração do conteúdo da linha) - requer 10 vezes menos gravações que o InnoDB. Isso, obviamente, reduz a carga de E/S, mas, ainda mais importante, aumentará a vida útil de um SSD dez vezes em comparação com a entrega da mesma carga usando o InnoDB). Do ponto de vista do desempenho, quanto menor o volume de dados, mais rápido o acesso, portanto, mecanismos de armazenamento como esse também podem ajudar a obter os dados do banco de dados mais rapidamente (mesmo que não tenha sido a prioridade mais alta ao projetar MyRocks).
Armazenamentos de dados colunares
Recursos relacionados Gerenciamento de desempenho do ClusterControl Compreendendo os efeitos da alta latência na alta disponibilidade Soluções MySQL e MariaDB Folha de referência de desempenho do MySQLEm algum momento, tudo o que podemos fazer é admitir que não podemos lidar com esse volume de dados usando o MySQL. Claro, você pode fragmentá-lo, você pode fazer coisas diferentes, mas eventualmente não faz mais sentido. É hora de procurar soluções adicionais. Uma delas seria usar datastores colunares - bancos de dados projetados com a análise de big data em mente. Claro, eles não ajudarão com o tipo de tráfego OLTP, mas as análises são praticamente padrão hoje em dia, pois as empresas tentam ser orientadas por dados e tomar decisões com base em números exatos, não em dados aleatórios. Existem vários armazenamentos de dados colunares, mas gostaríamos de mencionar aqui dois deles. MariaDB AX e ClickHouse. Temos alguns blogs explicando o que é o MariaDB AX e como o MariaDB AX pode ser usado. O que é importante, o MariaDB AX pode ser dimensionado em forma de cluster, melhorando o desempenho. O ClickHouse é outra opção para executar análises - o ClickHouse pode ser facilmente configurado para replicar dados do MySQL, conforme discutimos em uma de nossas postagens no blog. É rápido, gratuito e também pode ser usado para formar um cluster e fragmentar dados para um desempenho ainda melhor.
Conclusão
Esperamos que esta postagem do blog tenha lhe dado insights sobre como grandes volumes de dados podem ser tratados no MySQL ou MariaDB. Felizmente, existem algumas opções à nossa disposição e, eventualmente, se não conseguirmos realmente fazer funcionar, existem boas alternativas.