O DuckDB é conhecido como o “SQLite para Analytics”. Quando integrado ao ecossistema Laravel dentro de containers, ele permite realizar consultas pesadas e exportações massivas sem degradar a performance do seu banco de dados principal.
1. Arquitetura da Solução Link para o cabeçalho
A estratégia consiste em rodar o DuckDB via CLI dentro do container PHP, comunicando-se com o container MySQL através da rede interna do Docker.
2. Configuração do Ambiente (Docker & Dockerfile) Link para o cabeçalho
O maior desafio é garantir que as extensões do DuckDB estejam disponíveis offline. No Dockerfile, instalamos o binário e pré-configuramos o diretório de extensões.
### Dockerfile (Trecho DuckDB)
# Instalação do DuckDB CLI
ARG DUCKDB_VERSION="v1.4.3"
ENV DUCKDB_EXTENSION_DIRECTORY=/app/duckdb/extensions
RUN wget https://github.com/duckdb/duckdb/releases/download/${DUCKDB_VERSION}/duckdb_cli-linux-amd64.zip -O /tmp/duckdb.zip && \
dnf install -y unzip gzip && \
unzip /tmp/duckdb.zip -d /usr/local/bin/ && \
chmod +x /usr/local/bin/duckdb && \
rm /tmp/duckdb.zip
# Estrutura para Extensões e Banco de Dados
RUN mkdir -p /app/duckdb/database && chmod 777 /app/duckdb/database
RUN mkdir -p /app/duckdb/extensions
RUN chmod -R 777 /app/duckdb/extensions
RUN duckdb -c "SET extension_directory='/app/duckdb/extensions'; INSTALL mysql;"
No docker-compose.yml, garantimos a persistência:
services:
php74:
volumes:
- .:/app
- /app/duckdb/extensions # Volume anônimo para proteger extensões do build
- ./.docker/duckdb/database:/app/duckdb/database
3. Implementação: Repository e Service Link para o cabeçalho
Para manter o código limpo (Clean Code), separamos a execução do shell da lógica de negócio.
DuckDbRepository.php Link para o cabeçalho
Este arquivo lida com a “sujeira” de executar comandos via proc_open e garante que a porta do MySQL seja tratada como inteiro para evitar o erro stoi.
public function execute(string $query): array {
$port = (int) env('DB_PORT', 3306);
$fullSql = "
SET extension_directory='/app/duckdb/extensions';
SET max_memory='1.5GB'; -- Configuração de limite de memória inserida aqui
SET threads=2; -- Limita a 2 núcleos de processamento
LOAD mysql_scanner;
ATTACH 'host=".env('DB_HOST')." user=".env('DB_USERNAME')." password=".env('DB_PASSWORD')." port=$port database=".env('DB_DATABASE')."' AS my_db (TYPE MYSQL_SCANNER);
$query
";
// Execução via proc_open e retorno de json_decode...
}
DuckDbAnalyticsService.php Link para o cabeçalho
A camada de serviço expõe métodos de alto nível para a aplicação.
public function getTableInventory(): array {
return $this->duckDb->execute("SELECT table_name FROM information_schema.tables WHERE table_schema = 'db_app';");
}
public function getUserStats(): array {
return $this->duckDb->execute("SELECT count(*) as total FROM my_db.users;");
}
4. Testes de Evolução do Código Link para o cabeçalho
Abaixo, a jornada de como o código evoluiu de um comando simples para uma estrutura profissional.
Fase 1: TestDuckDB (O Teste de Sanidade) Link para o cabeçalho
O primeiro teste apenas valida se o binário está acessível e executando funções básicas de sistema.
-
Comando:
duckdb -c "SELECT 1;" -
Objetivo: Validar se o
shell_execdo PHP consegue invocar o DuckDB.
Fase 2: TestDuckDBToMysql (A Ponte Direta) Link para o cabeçalho
Nesta fase, testamos a conexão direta no comando. Foi aqui que descobrimos a necessidade de usar o mysql_scanner e o ATTACH correto.
-
Desafio: Resolver o download da extensão e a conectividade de rede.
-
Resultado: Sucesso ao listar tabelas via
information_schema.
Fase 3: TestDuckDBMysqlService (A Estrutura Final) Link para o cabeçalho
Aqui, o comando Laravel torna-se apenas um “cliente” do Service.
public function handle(DuckDbAnalyticsService $service) {
$this->info('🔗 Iniciando via Service/Repository...');
$tabelas = $service->getTableInventory();
$this->table(['Tabela'], $tabelas);
}
5. Conclusão e Próximos Passos Link para o cabeçalho
Com essa estrutura, você superou os obstáculos de permissões Docker, instalação de extensões offline e erros de conversão de tipos (stoi). Agora, sua aplicação está pronta para:
-
Gerar arquivos Parquet: Transformar tabelas MySQL em arquivos locais ultra-compactos.
-
Dashboards Pesados: Consultar milhões de linhas em milissegundos.
-
Data Warehouse Local: Manter um histórico de dados sem onerar o banco de produção.