10 pontos por GN⁺ 2024-01-16 | 1 comentários | Compartilhar no WhatsApp

Ativar/desativar o modo Wal2

  • O modo "Wal2" do SQLite é muito semelhante ao modo "wal (Write-Ahead Logging)".
  • Para mudar o banco de dados para o modo wal2, use o comando PRAGMA journal_mode = wal2;.
  • Não é possível mudar diretamente do modo "wal" para o modo "wal2"; primeiro é preciso mudar para o modo rollback.
  • Para mudar um banco de dados no modo wal para o modo wal2, use PRAGMA journal_mode = delete; e depois o comando PRAGMA journal_mode = wal2;.
  • Um banco de dados no modo wal2 só pode ser acessado por uma versão do SQLite compilada a partir desse branch.
  • Ao tentar usar outra versão do SQLite, ocorre o erro SQLITE_NOTADB.
  • Para mudar um banco de dados no modo wal2 para o modo rollback e permitir acesso por todas as versões do SQLite, use o comando PRAGMA journal_mode = delete;.

Vantagens do modo Wal2

  • No modo wal tradicional, quando o gravador escreve dados no banco de dados, ele não modifica diretamente o arquivo do banco, e sim acrescenta novos dados ao arquivo "-wal".
  • As operações de leitura leem dados tanto do arquivo original do banco de dados quanto do arquivo "-wal".
  • Em algum momento, os dados do arquivo "-wal" são copiados para o arquivo do banco de dados; isso é chamado de "checkpoint".
  • O checkpoint pode ser executado explicitamente por meio de PRAGMA wal_checkpoint ou sqlite3_wal_checkpoint_v2(), ou automaticamente ao configurar PRAGMA wal_autocheckpoint (configuração padrão).
  • O checkpointer não bloqueia o gravador, e o gravador também não bloqueia o checkpointer.
  • Porém, se o gravador escrever no banco durante um checkpoint, novos dados serão acrescentados ao fim do arquivo wal, e o arquivo wal pode continuar crescendo.
  • No modo wal2, não existe o problema de o arquivo wal crescer indefinidamente, mesmo que o checkpointer não tenha chance de concluir sem ser interrompido.
  • No modo wal2, são usados dois arquivos wal ("-wal" e "-wal2") em vez de um.
  • Quando os dados são gravados, o gravador começa acrescentando novos dados ao primeiro arquivo wal.
  • Quando o primeiro arquivo wal cresce o suficiente, o gravador passa a acrescentar dados ao segundo arquivo wal.
  • Depois disso, o primeiro arquivo wal pode passar por checkpoint e, quando o segundo arquivo wal também crescer o suficiente e o primeiro arquivo tiver passado por checkpoint, volta-se ao primeiro arquivo.

Programação de aplicações

  • Do ponto de vista do usuário, a principal diferença entre os modos wal e wal2 está relacionada ao checkpoint.
  • No modo wal, é possível tentar um checkpoint a qualquer momento, mas no modo wal2 o checkpoint só é possível depois que o gravador alterna para o "outro" arquivo wal.
  • No modo wal, após o commit de uma transação, o wal-hook (callback) é chamado usando como argumento o número total de páginas do arquivo wal.
  • No modo wal2, o wal-hook é chamado usando como argumento o total de páginas ainda não submetidas a checkpoint nos dois arquivos wal, ou 0 caso o "outro" arquivo wal esteja vazio ou já tenha passado por checkpoint.
  • Recomenda-se que clientes usem no banco de dados em modo wal2 a mesma estratégia de checkpoint usada no modo wal.
  • O wal-hook é chamado depois que a transação é confirmada em disco e o bloqueio do banco de dados é liberado, mas isso ocorre dentro da chamada sqlite3_step().
  • Em sistemas BEGIN CONCURRENT, em vez de executar o checkpoint dentro do wal-hook, pode-se usar uma thread que adie esse trabalho até que o mutex da aplicação seja liberado.

Opinião do GN⁺

  • O modo wal2 do SQLite oferece uma nova forma de journaling que melhora a concorrência e a eficiência do banco de dados.
  • Resolver o problema de crescimento infinito do arquivo wal é importante para melhorar a estabilidade e o desempenho do sistema.
  • Com a adoção do modo wal2, os desenvolvedores precisam rever a estratégia de checkpoint do banco de dados e implementar uma lógica de checkpoint adequada para obter melhor concorrência.

1 comentários

 
GN⁺ 2024-01-16
Comentários no Hacker News
  • No modo WAL2, dois arquivos WAL são usados em vez de um. Os arquivos são nomeados como "<database>-wal" e "<database>-wal2". Quando dados são gravados no banco de dados, a operação de escrita começa acrescentando novos dados ao primeiro arquivo WAL. Quando o primeiro arquivo WAL fica grande o suficiente, a escrita passa a acrescentar dados ao segundo arquivo WAL. Nesse ponto, o primeiro arquivo WAL pode passar por checkpoint (e depois ser sobrescrito) e, quando o segundo arquivo WAL fica grande o suficiente e o primeiro já foi checkpointado, a escrita volta para o primeiro arquivo WAL. Esse processo continua.

    • Essa abordagem é tão lógica que eu não entendo por que o modo WAL não foi implementado assim desde o começo. Talvez tenha sido considerada uma otimização prematura.
    • Espero que esse modo fique disponível de forma geral.
  • Bedrock

    • Bedrock é um branch mais interessante.
    • Inclui os recursos WAL2 + CONCURRENT.
    • É o branch que a Expensify usa para escalar até 4M QPS em um único nó (há 6 anos).
  • Link para o left-right primitive, uma técnica semelhante ao modo WAL2.

    • Essa técnica é mais antiga do que a implementação vinculada, mas foi redescoberta independentemente e, em particular, escrita para dar suporte a outro banco de dados SQL de alto desempenho chamado Noria.
  • No modo WAL2, dois arquivos WAL são usados em vez de um. Os arquivos são nomeados como "<database>-wal" e "<database>-wal2".

    • Fico imaginando quantas pessoas vão apagar o arquivo wal, porque podem pensar que, ao mudar para wal2, o arquivo wal ficou sobrando.
  • O Microsoft SQL Server usa uma arquitetura semelhante, mas aloca Virtual Log Files (VLF) dentro do arquivo de log físico (no disco), em vez de usar arquivos de log separados. Os VLF são alocados em um ring buffer e podem chegar a milhares.

  • Dá para perceber que esse recurso ainda não foi lançado.

  • Sempre me preocupei com o fato de que o WAL existe para ajudar a manter a integridade dos dados e permitir a recuperação após falhas. Mas o próprio arquivo é gravado em lotes (confirmado de forma confiável no disco), e não após cada alteração no banco de dados, para ganhar desempenho. Isso não compromete o objetivo? De forma mais geral, sem se limitar a bancos de dados, nunca encontrei uma resposta para isso.

  • Fico curioso sobre como isso vai afetar novos sistemas distribuídos de SQLite, como o Litestream.

  • Então isso é basicamente double buffering para banco de dados? Faz sentido.

  • O modo WAL2 foi incluído nos benchmarks da pesquisa sobre o backend HC-tree.