Quais são as características de desempenho do sqlite com arquivos de banco de dados muito grandes? [fechadas]

325

Eu sei que o sqlite não funciona bem com arquivos de banco de dados extremamente grandes, mesmo quando eles são suportados (costumava haver um comentário no site do sqlite informando que, se você precisar de tamanhos de arquivo acima de 1 GB, poderá considerar o uso de rdbms corporativos. não encontrar mais, pode estar relacionado a uma versão mais antiga do sqlite).

No entanto, para meus propósitos, gostaria de ter uma idéia de quão ruim é realmente antes de considerar outras soluções.

Estou falando de arquivos de dados sqlite na faixa de vários gigabytes, de 2 GB em diante. Alguém tem alguma experiência com isso? Alguma dica / idéia?

Snazzer
fonte
1
Usando rosqueamento (conexão por thread) pode ajudar apenas para leitura - stackoverflow.com/a/24029046/743263
Malkia
23
Ano de 2016: Eu tenho um banco de dados de 5 GB que roda em SQLite sem problemas. Eu instalei exatamente o mesmo conjunto de dados no Postgres. O SQLite executou uma consulta complexa em 2,7 ms, o Postgres em 2,5 ms. Acabei no Postgres para facilitar o acesso ao Regex e melhores recursos de índice. Mas fiquei impressionado com o SQLite e também poderia tê-lo usado.
paulb

Respostas:

246

Então fiz alguns testes com o sqlite para arquivos muito grandes e cheguei a algumas conclusões (pelo menos para minha aplicação específica).

Os testes envolvem um único arquivo sqlite com uma única tabela ou várias tabelas. Cada tabela tinha cerca de 8 colunas, quase todos os números inteiros e 4 índices.

A idéia era inserir dados suficientes até que os arquivos sqlite tivessem cerca de 50 GB.

Mesa Única

Tentei inserir várias linhas em um arquivo sqlite com apenas uma tabela. Quando o arquivo tinha cerca de 7 GB (desculpe, não posso ser específico sobre a contagem de linhas), as inserções estavam demorando muito. Eu havia estimado que meu teste para inserir todos os meus dados levaria 24 horas ou mais, mas não foi concluído mesmo após 48 horas.

Isso me leva a concluir que uma única tabela sqlite muito grande terá problemas com inserções e provavelmente outras operações também.

Acho que isso não é surpresa, pois a tabela fica maior, a inserção e a atualização de todos os índices levam mais tempo.

Tabelas Múltiplas

Tentei dividir os dados por tempo em várias tabelas, uma tabela por dia. Os dados da tabela 1 original foram divididos em ~ 700 tabelas.

Essa configuração não teve problemas com a inserção, não demorou mais com o passar do tempo, pois uma nova tabela era criada para todos os dias.

Questões de vácuo

Conforme apontado por i_like_caffeine, o comando VACUUM é um problema, quanto maior o arquivo sqlite. À medida que mais inserções / exclusões são feitas, a fragmentação do arquivo no disco fica pior, portanto, o objetivo é periodicamente VACUUM para otimizar o arquivo e recuperar o espaço no arquivo.

No entanto, como indicado na documentação , é feita uma cópia completa do banco de dados para fazer um vácuo, levando muito tempo para ser concluída. Portanto, quanto menor o banco de dados, mais rápida será a conclusão dessa operação.

Conclusões

Para meu aplicativo específico, provavelmente estarei dividindo dados em vários arquivos db, um por dia, para obter o melhor desempenho de vácuo e velocidade de inserção / exclusão.

Isso complica as consultas, mas, para mim, é uma compensação valiosa poder indexar tantos dados. Uma vantagem adicional é que eu posso excluir um arquivo db inteiro para descartar os dados de um dia (uma operação comum para o meu aplicativo).

Eu provavelmente teria que monitorar o tamanho da tabela por arquivo também para ver quando a velocidade se tornará um problema.

É uma pena que não pareça haver um método de vácuo incremental que não seja o vácuo automático . Não posso usá-lo porque meu objetivo para o vácuo é desfragmentar o arquivo (o espaço no arquivo não é grande coisa), o que o vácuo automático não faz. De fato, a documentação afirma que isso pode piorar a fragmentação, por isso tenho que recorrer periodicamente a um vácuo total no arquivo.

Snazzer
fonte
5
Informação muito útil. É pura especulação, mas gostaria de saber se a nova API de backup pode ser usada para criar uma versão não fragmentada do seu banco de dados diariamente e evitar a necessidade de executar um VACUUM.
Eodonohoe # 03/09
24
Estou curioso, todos os seus INSERTS estavam em uma transação?
1211 Paul Lefebvre
9
Sim, as inserções foram feitas em lotes de 10000 mensagens por transação.
Snazzer 14/05/09
6
Qual sistema de arquivos você usou? Se ext {2,3,4}, qual era a configuração data =, o registro no diário foi ativado? Além dos padrões io, a maneira como o sqlite libera o disco pode ser significativa.
Tobu 22/02
5
Eu estava testando principalmente no Windows, por isso não posso comentar sobre o comportamento no Linux.
Snazzer 09/03/11
169

Estamos usando DBS de mais de 50 GB em nossa plataforma. não reclama funciona muito bem. Verifique se você está fazendo tudo certo! Você está usando instruções predefinidas? * SQLITE 3.7.3

  1. Transações
  2. Declarações pré-feitas
  3. Aplique essas configurações (logo após criar o banco de dados)

    PRAGMA main.page_size = 4096;
    PRAGMA main.cache_size=10000;
    PRAGMA main.locking_mode=EXCLUSIVE;
    PRAGMA main.synchronous=NORMAL;
    PRAGMA main.journal_mode=WAL;
    PRAGMA main.cache_size=5000;
    

Espero que isso ajude os outros, funciona muito bem aqui

Alex
fonte
22
Recentemente testado com dbs na faixa de 160 GB, também funciona muito bem.
Snazzer
10
Também PRAGMA main.temp_store = MEMORY;.
Vikrant Chaudhary
40
@ Alex, por que existem dois PRAGMA main.cache_size = 5000 ;?
Jack
23
Não aplique cegamente essas otimizações. Em particular síncrona = NORMAL não é à prova de falhas. Ou seja, uma falha no processo no momento certo pode corromper seu banco de dados, mesmo na ausência de falhas no disco. sqlite.org/pragma.html#pragma_synchronous
mpm
22
@Alex, você pode explicar esses valores e a diferença entre eles e os padrões?
4m1nh4j1
65

Criei bancos de dados SQLite com tamanho de até 3,5 GB sem problemas visíveis de desempenho. Se bem me lembro, acho que o SQLite2 pode ter alguns limites mais baixos, mas não acho que o SQLite3 tenha esses problemas.

De acordo com a página Limites do SQLite , o tamanho máximo de cada página do banco de dados é 32K. E o máximo de páginas em um banco de dados é 1024 ^ 3. Então, pela minha matemática, chega a 32 terabytes como o tamanho máximo. Eu acho que você atingirá os limites do seu sistema de arquivos antes de atingir o SQLite!

Paul Lefebvre
fonte
3
Dependendo quais operações que você está executando, tentando apagar 3000 linhas no banco de dados de um 8G sqlite, é preciso tempo suficiente para você amadurecer um bom pote de imprensa francesa, lol
benjaminz
4
@ Benjaminjaminz, você deve estar fazendo errado. Se você agrupar a exclusão de 3 mil linhas em uma transação, deve ser quase instantâneo. Eu mesmo tive esse erro: excluir 10k linhas uma a uma levou 30 minutos. Mas depois que agrupei todas as instruções de exclusão em uma transação, foram necessários 5s.
Mvp
55

Muito do motivo que levou mais de 48 horas para fazer suas inserções é por causa de seus índices. É incrivelmente mais rápido:

1 - Solte todos os índices 2 - Faça todas as inserções 3 - Crie índices novamente

user352992
fonte
23
Isso é bem conhecido ... mas, por um longo processo, você não descartará periodicamente seus índices para reconstruí-los, especialmente quando você os consultará para fazer o trabalho. Essa é a abordagem adotada, porém, quando o sqlite db precisar ser reconstruído do zero, os índices serão criados depois que todas as inserções forem concluídas.
Snazzer 28/05
24
@ Snazzer em uma situação semelhante, usamos uma tabela "acumuladora": uma vez por dia, movíamos as linhas acumuladas da tabela acumuladora para a tabela principal em uma única transação. Onde necessário, uma visualização cuidava da apresentação de ambas as tabelas como uma única tabela.
CAFxX
4
Outra opção é manter os índices, mas pré-classifique os dados em ordem de índice antes de inseri-los.
Steven Kryskalla
1
@StevenKryskalla, como isso se compara a descartar os índices e recriá-los? Algum link que você conhece comparou?
Mcmillab
1
@mcmillab Isso foi anos atrás, então eu não lembro de todos os detalhes ou estatísticas de referência, mas pensando intuitivamente, inserir N elementos ordenados aleatoriamente em um índice levará tempo O (NlogN), enquanto a inserção de N elementos classificados levará O (N ) Tempo.
Steven Kryskalla 6/02/19
34

Além da recomendação usual:

  1. Solte o índice para inserção em massa.
  2. Inserções / atualizações em lote em grandes transações.
  3. Ajuste o cache do buffer / desative o diário / w PRAGMAs.
  4. Use uma máquina de 64 bits (para poder usar muito cache ™).
  5. [adicionado em julho de 2014] Use a expressão de tabela comum (CTE) em vez de executar várias consultas SQL! Requer o SQLite versão 3.8.3.

Aprendi o seguinte com minha experiência com o SQLite3:

  1. Para obter velocidade máxima de inserção, não use esquema com nenhuma restrição de coluna. (Altere a tabela posteriormente, conforme necessário Você não pode adicionar restrições com ALTER TABLE).
  2. Otimize seu esquema para armazenar o que você precisa. Às vezes, isso significa quebrar tabelas e / ou até compactar / transformar seus dados antes de inserir no banco de dados. Um ótimo exemplo é armazenar endereços IP como números inteiros (longos).
  3. Uma tabela por arquivo db - para minimizar a contenção de bloqueio. (Use ATTACH DATABASE se você quiser ter um único objeto de conexão.
  4. O SQLite pode armazenar diferentes tipos de dados na mesma coluna (digitação dinâmica), use isso para sua vantagem.

Pergunta / comentário bem-vindo. ;-)

Lester Cheung
fonte
1
Quanto impacto você recebe de 'uma tabela por arquivo db'? Soa interessante. Você acha que isso importaria muito se sua mesa tivesse apenas 3 mesas e estivesse sendo construída a partir do zero?
Martin Velez
4
@martin odeio dizer isso, mas a resposta é que depende . A ideia é particionar os dados em tamanho gerenciável. No meu caso de uso, coleto dados de diferentes hosts e faço relatórios sobre os dados após o fato, para que essa abordagem funcione bem. A partição por data / hora, conforme sugerido por outros, deve funcionar bem para dados que abrangem um longo período de tempo, eu imagino.
Lester Cheung #
3
@Lester Cheung: Em relação ao seu segundo número 1: dos documentos e da experiência pessoal, até hoje, o SQLite3 não oferece suporte à adição de restrições ao ALTER TABLE após a criação da tabela. A única maneira de adicionar ou remover restrições das linhas de tabela existentes é criar uma nova tabela com as características desejadas e copiar sobre todas as linhas, o que provavelmente será muito mais lento do que inserir uma vez com restrições.
Mumbleskates
3
@Widdershins, você está absolutamente certo - ALTER TABLE no SQLite não permite adicionar restrições. Não sei o que estava fumando - atualizará a resposta - obrigado.
Lester Cheung
Nenhuma dessas sugestões tem nada a ver com o uso de enormes arquivos db SQLite. A pergunta foi editada desde que esta resposta foi enviada?
A. Rager
9

Eu acho que as principais reclamações sobre o dimensionamento do sqlite são:

  1. Gravação de processo único.
  2. Sem espelhamento.
  3. Sem replicação.
Desconhecido
fonte
9

Eu tenho um banco de dados SQLite de 7 GB. Para executar uma consulta específica com uma junção interna, são necessários 2,6s. Para acelerar isso, tentei adicionar índices. Dependendo de quais índices eu adicionei, algumas vezes a consulta caiu para 0,1s e outras vezes subiu para 7s. Acho que o problema no meu caso foi que, se uma coluna é altamente duplicada, a adição de um índice prejudica o desempenho :(

Mike Oxynormas
fonte
9
Por que uma coluna com muitas duplicatas prejudicaria o desempenho (pergunta séria)?
Martin Velez
6
uma coluna com baixa cardinalidade é mais difícil de indexar: stackoverflow.com/questions/2113181/…
metrix
9

Costumava haver uma declaração na documentação do SQLite de que o limite prático de tamanho de um arquivo de banco de dados era de algumas dezenas de GB: s. Isso ocorreu principalmente devido à necessidade do SQLite "alocar um bitmap de páginas sujas" sempre que você iniciou uma transação. Assim, 256 bytes de RAM foram necessários para cada MB no banco de dados. A inserção em um arquivo DB de 50 GB exigiria um alto (2 ^ 8) * (2 ^ 10) = 2 ^ 18 = 256 MB de RAM.

Mas, como nas versões recentes do SQLite, isso não é mais necessário. Leia mais aqui .

Alix Axel
fonte
25
Eu sinto muito que eu tenho que apontar isso, mas 2^18é na verdade apenas 256 K.
Gabriel Schreiber
7
@GabrielSchreiber isso e também o fato de que 50 GB não são (2 ^ 10) MB, são apenas 1 GB. Assim, para um banco de dados de 50GB, você precisa 12.5MB de memória: (2 ^ 8) * (2
elipoultorak
8

Eu tive problemas com grandes arquivos sqlite ao usar o comando vacuum.

Ainda não testei o recurso auto_vacuum. Se você espera atualizar e excluir dados com frequência, vale a pena examinar.

eodonohoe
fonte