Parte 1: Introdução e Arquitetura do Sistema
Descubra os fundamentos por trás de engines de banco de dados com uma implementação didática em Elixir, passo a passo, desde a arquitetura até consultas SQL.
Nesta série, seguiremos passo a passo o tutorial disponível em:
para construir do zero um sistema de banco de dados simplificado em Elixir. Nosso objetivo é entender os conceitos fundamentais por trás de engines como PostgreSQL, mas com implementações didáticas.
Objetivos Gerais da Série
- Implementar o mecanismo de armazenamento (storage engine) baseado em páginas fixas.
- Criar um parser SQL básico usando NimbleParsec para converter consultas em AST.
- Desenvolver um planner simples que gere planos de execução a partir da AST.
- Construir um executor capaz de realizar operações de leitura e escrita em arquivos de dados.
- Adicionar uma estrutura de índices (ex.: B-Tree) para acelerar buscas.
- Oferecer uma interface REPL para executar comandos SQL interativamente.
1. Arquitetura Geral do Sistema
Inspirados no repositório “build-a-simple-database-from-scratch” (zsxh), adotaremos uma arquitetura em camadas que abrange os seguintes subsistemas:
- Catalog Manager (DB.Catalog): mantém metadados sobre tabelas, colunas, índices e esquemas. Operações de criação, alteração e consulta de objetos do banco.
- Storage Manager (DB.Storage): gerencia o acesso físico ao arquivo de dados, baseado em páginas fixas. Inclui:
- Buffer Pool (DB.Storage.Buffer): cache de páginas em memória, com política de substituição LRU.
- Disk Manager (DB.Storage.Disk): leitura/escrita de páginas no disco.
- Record Manager (DB.Storage.Record): empacota e desempacota tuplas em páginas, cuida de offsets e free space.
- Index Manager (DB.Index): implementação de B-Tree simplificado, suportando pesquisas e inserções em índices.
- Transaction Manager (DB.Transaction): controle de concorrência (locks simples) e gerenciamento de logs para recuperação básica (Write-Ahead Logging).
- Parser (DB.Parser): converte consultas SQL em AST usando NimbleParsec.
- Optimizer/Planner (DB.Planner): gera planos lógicos e físicos (Seq Scan, Index Scan), com estimativas de custo simples.
- Executor (DB.Executor): percorre o plano de execução, chama Record Manager e Index Manager para acesso aos dados.
- SQL Engine (DB.Engine): coordena Parser → Planner → Executor, gerencia transações e tratamento de erros.
- CLI (DB.CLI): interface REPL para interação do usuário, envio de comandos e exibição de resultados.
Cada camada se comunica apenas com as adjacentes, facilitando testes unitários e evolução de funcionalidades. No próximo tópico, veremos como inicializar o projeto e estruturar cada módulo antes de codificar.
2. Setup Inicial do Projeto
- Crie o projeto supervisionado:
mix new mini_db --sup
- Adicione ao
mix.exs
:defp deps do [ {:nimble_parsec, "~> 1.0"} ] end
- Estrutura de pastas em:
- Ajuste o
application.ex
para supervisionar os componentes necessários.
Próximos Passos
Na Parte 2, começaremos pela implementação do DB.Storage, definindo o formato de página e as funções de leitura/escrita.
Referências
- Video: “Building a Simple Database Engine from Scratch”. Disponível em: https://www.youtube.com/watch?v=TaZz4SRVZkU
- https://github.com/zsxh/build-a-simple-database-from-scratch
- KLEPPMANN, Martin. Designing Data-Intensive Applications. O’Reilly Media, 2017.
- STONEBRAKER, Michael et al. Readings in Database Systems (“Red Book”). 5ª ed., MIT Press, 2018.
- GARCIA-MOLINA, Hector; ULLMAN, Jeffrey D.; WIDOM, Jennifer. Database Systems: The Complete Book. 2ª ed., Pearson, 2008.
- Documentação NimbleParsec. Disponível em: https://hexdocs.pm/nimble_parsec/NimbleParsec.html