Fragmentação de Dados MySQL com ShardingSphere-JDBC

A fragmentação de dados, ou sharding, é uma abordagem essencial para escalar sistemas de banco de dados relacionais, superando os gargalos de desempenho e armazenamento de uma única instância. O Apache ShardingSphere-JDBC, um framework Java leve e transparente, facilita a implementação dessa técnica. Este guia apresenta um roteiro prático para aplicar o ShardingSphere-JDBC com MySQL.

Conceitos Fundamentais de Fragmentação

  1. Chave de Fragmentação (Sharding Key)
    É o campo selecionado em uma tabela para determinar onde os dados serão armazenados. Sua escolha é crucial: deve possuir alta cardinalidade e distribuição uniforme para evitar desequilíbrio na distribuição dos dados. Por exemplo, um ID de usuário.

    Matematicamente, para uma chave de fragmentação $k$ e $n$ fragmentos, a posição do fragmento $p$ é calculada como $p = k \pmod n$.

  2. Estratégias de Fragmentação

    • Fragmentação Horizontal de Tabelas (Horizontal Table Sharding): Divide uma tabela grande em múltiplas tabelas menores, mantendo a mesma estrutura de esquema. Exemplo: pedidos_0, pedidos_1.
    • Fragmentação Vertical de Bancos de Dados (Vertical Database Sharding): Separa um único banco de dados em vários, tipicamente baseados em domínios de negócio. Exemplo: um banco de dados para usuários e outro para pedidos.

Configuração Prática do ShardingSphere-JDBC

1. Adição de Dependência (Maven)

Para integrar o ShardingSphere-JDBC ao seu projeto Spring Boot, adicione a seguinte dependência ao seu pom.xml:

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>5.1.0</version>
</dependency>

2. Configuração de Fragmentação (YAML)

Defina as regras de fragmentação no arquivo application.yml. Este exemplo configura dois bancos de dados físicos e fragmenta uma tabela lógica t_pedido em tabelas físicas dentro desses bancos de dados.

spring:
  shardingsphere:
    datasource:
      names: ds_master, ds_replica  # Nomes dos bancos de dados físicos
      ds_master:
        url: jdbc:mysql://db-host-1:3306/db_principal?useSSL=false
        username: usuario_bd
        password: senha_segura
      ds_replica:
        url: jdbc:mysql://db-host-2:3306/db_principal?useSSL=false
        username: usuario_bd
        password: senha_segura
    rules:
      sharding:
        tables:
          t_pedido:  # Nome da tabela lógica
            actual-data-nodes: ds_$->{master,replica}.t_pedido_$->{0..3}  # Mapeamento para tabelas físicas: ds_master.t_pedido_0, ds_master.t_pedido_1 ...
            database-strategy:
              standard:
                sharding-column: id_cliente
                sharding-algorithm-name: alg_bd_hash_modulo
            table-strategy:
              standard:
                sharding-column: id_pedido
                sharding-algorithm-name: alg_tabela_hash_modulo
        sharding-algorithms:
          alg_bd_hash_modulo:
            type: HASH_MOD  # Algoritmo de fragmentação de banco de dados
            props:
              sharding-count: 2  # Número de bancos de dados fragmentados
          alg_tabela_hash_modulo:
            type: HASH_MOD  # Algoritmo de fragmentação de tabela
            props:
              sharding-count: 4  # Número de tabelas fragmentadas por banco de dados

3. Validação do Algoritmo de Fragmentação

Para ilustrar a lógica de roteamento:

  • Se id_cliente = 11: Posição do banco de dados $p\_{\text{bd}} = 11 \pmod 2 = 1$ → Roteado para ds_replica
  • Se id_pedido = 205: Posição da tabela $p\_{\text{tabela}} = 205 \pmod 4 = 1$ → Roteado para t_pedido_1

Assim, um pedido com id_cliente=11 e id_pedido=205 seria armazenado em ds_replica.t_pedido_1.

Exemplo de Código da Aplicação

1. Classe de Entidade (JPA/MyBatis)

Defina a entidade Java, mapeando para a tabela lógica.

import lombok.Data;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.TableName; // Exemplo com MyBatis-Plus

@Data
@TableName("t_pedido") // Nome da tabela lógica no ShardingSphere
public class Pedido {
    private Long idPedido;
    private Long idCliente;
    private BigDecimal valorTotal;
}

2. Inserção de Dados

O ShardingSphere-JDBC intercepta automaticamente as operações SQL e as roteia para o fragmento correto com base nas chaves de fragmentação definidas.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ServicoPedido {

    @Autowired
    private RepositorioPedido repositorioPedido; // Interface/Mapper para operações de Pedido

    public void registrarNovoPedido(Pedido novoPedido) {
        // O ShardingSphere-JDBC roteará esta inserção para o banco/tabela correto
        repositorioPedido.salvar(novoPedido);
    }
}

Considerações Importantes

1. Limitações da Chave de Fragmentação

  • A chave de fragmentação deve estar presente nas cláusulas WHERE das suas consultas para que o ShardingSphere-JDBC possa rotear a query eficientemente. A ausência da chave pode resultar em varreduras de todas as tabelas e bancos de dados (full-table scan, broadcast queries), impactando o desempenho.
  • Evite usar campos com baixa cardinalidade (poucos valores distintos), como 'gênero', como chave de fragmentação, pois podem levar a uma distribuição desequilibrada de dados.

2. Transações Distribuídas

Para garantir a consistência de dados em operações que abrangem múltiplos fragmentos (bancos de dados), é essencial implementar uma solução de transação distribuída. O ShardingSphere-JDBC oferece suporte a protocolos como XA ou pode ser integrado com frameworks como Seata.

spring:
  shardingsphere:
    props:
      sql-show: true  # Exibe as instruções SQL reais executadas
      sql-simple: true
    transaction:
      type: XA  # Ativa o modo de transação XA

3. Estratégias de Escalabilidade

Planejar a expansão futura do sistema é crucial:

  • Método de Expansão em Dobro: Adiciona-se o dobro de fragmentos existentes. Durante a migração de dados, tanto os fragmentos antigos quanto os novos são considerados, permitindo uma transição gradual.
  • Hash Consistente: Uma técnica que minimiza a quantidade de dados a serem realocados quando novos fragmentos são adicionados ou removidos.

Comparativo de Performance

A fragmentação de dados pode proporcionar ganhos significativos de performance. Abaixo, um exemplo de desempenho em um cenário hipotético com 2 bancos de dados e 4 tabelas por banco, rodando MySQL 8.0 em um cluster de servidores de 16 núcleos e 32GB de RAM.

Cenário QPS Monolítico QPS Fragmentado
Inserção de Pedidos 1300 5200 (+300%)
Consulta de Pedidos 850 3400 (+300%)

A implementação correta da fragmentação com ShardingSphere-JDBC é um passo fundamental para construir aplicações capazes de gerenciar grandes volumes de dados com alta performance.

Tags: ShardingSphere-JDBC MySQL Fragmentação de Banco de Dados java Spring Boot

Publicado em 7-25 16:49