Quali sono le caratteristiche prestazionali di sqlite con file di database molto grandi? [chiuso]


325

So che sqlite non funziona bene con file di database estremamente grandi anche quando sono supportati (c'era un commento sul sito Web sqlite che affermava che se hai bisogno di file di dimensioni superiori a 1 GB potresti prendere in considerazione l'uso di un rdbms aziendale. non lo trovi più, potrebbe essere correlato a una versione precedente di sqlite).

Tuttavia, per i miei scopi, vorrei avere un'idea di quanto sia davvero grave prima di prendere in considerazione altre soluzioni.

Sto parlando di file di dati sqlite nell'intervallo multi-gigabyte, da 2 GB in poi. Qualcuno ha qualche esperienza con questo? Qualche consiglio / idea?


1
Utilizzando threading (connessione per thread) potrebbe aiutare solo per la lettura - stackoverflow.com/a/24029046/743263
Malkia


23
Anno 2016: ho un database da 5 GB che funziona su SQLite senza problemi. Ho installato lo stesso set di dati esatto su Postgres. SQLite ha eseguito una query complessa in 2,7 ms, Postgres in 2,5 ms. Sono finito su Postgres per un accesso Regex più semplice e migliori funzionalità di indice. Ma sono rimasto colpito da SQLite e avrei potuto usarlo anche io.
Paolo

Risposte:


246

Quindi ho fatto alcuni test con sqlite per file molto grandi e sono giunto ad alcune conclusioni (almeno per la mia specifica applicazione).

I test riguardano un singolo file sqlite con una singola tabella o più tabelle. Ogni tabella aveva circa 8 colonne, quasi tutti numeri interi e 4 indici.

L'idea era quella di inserire dati sufficienti fino a quando i file sqlite erano circa 50 GB.

Tavolo singolo

Ho provato a inserire più righe in un file sqlite con una sola tabella. Quando il file era di circa 7 GB (mi dispiace, non posso essere specifico sui conteggi delle righe), gli inserimenti impiegavano troppo tempo. Avevo stimato che il mio test per inserire tutti i miei dati avrebbe richiesto circa 24 ore, ma non è stato completato nemmeno dopo 48 ore.

Questo mi porta a concludere che una singola tabella sqlite molto grande avrà problemi con gli inserimenti e probabilmente anche altre operazioni.

Immagino che questa non sia una sorpresa, poiché la tabella diventa più grande, l'inserimento e l'aggiornamento di tutti gli indici richiedono più tempo.

Tabelle multiple

Ho quindi provato a dividere i dati per tempo su più tabelle, una tabella al giorno. I dati per la 1 tabella originale sono stati divisi in ~ 700 tabelle.

Questa configurazione non ha avuto problemi con l'inserimento, non ha richiesto più tempo con il passare del tempo, poiché una nuova tabella è stata creata per ogni giorno.

Problemi di vuoto

Come sottolineato da i_like_caffeine, il comando VACUUM è un problema più grande è il file sqlite. Man mano che vengono fatti più inserimenti / eliminazioni, la frammentazione del file su disco peggiorerà, quindi l'obiettivo è periodicamente VACUUM per ottimizzare il file e recuperare spazio sul file.

Tuttavia, come sottolineato dalla documentazione , viene creata una copia completa del database per fare il vuoto, impiegando molto tempo per il completamento. Quindi, più piccolo è il database, più veloce sarà questa operazione.

conclusioni

Per la mia specifica applicazione, probabilmente dividerò i dati su più file db, uno al giorno, per ottenere il meglio sia dalle prestazioni del vuoto che dalla velocità di inserimento / cancellazione.

Ciò complica le query, ma per me è un compromesso utile poter indicizzare questi dati. Un ulteriore vantaggio è che posso semplicemente eliminare un intero file db per eliminare i dati di un giorno (un'operazione comune per la mia applicazione).

Probabilmente dovrei monitorare anche le dimensioni della tabella per file per vedere quando la velocità diventerà un problema.

Peccato che non ci sia un metodo di vuoto incrementale diverso dal vuoto automatico . Non posso usarlo perché il mio obiettivo per il vuoto è di deframmentare il file (lo spazio per i file non è un grosso problema), cosa che il vuoto automatico non fa. In effetti, la documentazione afferma che potrebbe peggiorare la frammentazione, quindi devo ricorrere periodicamente al completo vuoto del file.


5
Informazioni molto utili Pura speculazione ma mi chiedo se la nuova API di backup può essere utilizzata per creare una versione non frammentata del database su base giornaliera ed evitare la necessità di eseguire un VACUUM.
eodonohoe,

24
Sono curioso, tutti i tuoi INSERTI erano in una transazione?
Paul Lefebvre,

9
Sì, gli inserimenti sono stati eseguiti in batch di 10000 messaggi per transazione.
Snazzer,

6
Quale filesystem hai usato? Se ext {2,3,4}, qual era l'impostazione data =, il journaling era abilitato? Oltre ai modelli io, il modo in cui sqlite si scarica sul disco può essere significativo.
Tobu,

5
Stavo testando principalmente su Windows, quindi non posso commentare il comportamento su Linux.
Snazzer

169

Stiamo utilizzando DBS di 50 GB + sulla nostra piattaforma. nessuna lamentela funziona alla grande. Assicurati di fare tutto bene! Stai usando istruzioni predefinite? * SQLITE 3.7.3

  1. Le transazioni
  2. Dichiarazioni pre fatte
  3. Applica queste impostazioni (subito dopo aver creato il DB)

    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;
    

Spero che questo possa aiutare gli altri, funziona benissimo qui


22
Testato di recente con dbs nella gamma da 160 GB, funziona egregiamente.
Snazzer,

10
Inoltre PRAGMA main.temp_store = MEMORY;.
Vikrant Chaudhary,

40
@Alex, perché ci sono due PRAGMA main.cache_size = 5000 ;?
Jack

23
Non applicare ciecamente solo queste ottimizzazioni. In particolare sincrono = NORMALE non è sicuro per gli incidenti. Vale a dire, un arresto anomalo del processo al momento giusto può danneggiare il database anche in assenza di guasti del disco. sqlite.org/pragma.html#pragma_synchronous
mpm

22
@Alex, per favore, puoi spiegare quei valori e la differenza tra loro e quelli di default?
4m1nh4j1

65

Ho creato database SQLite con dimensioni fino a 3,5 GB senza evidenti problemi di prestazioni. Se ricordo bene, penso che SQLite2 avrebbe potuto avere dei limiti inferiori, ma non credo che SQLite3 abbia tali problemi.

Secondo la pagina Limiti SQLite , la dimensione massima di ciascuna pagina del database è 32 KB. E il numero massimo di pagine in un database è 1024 ^ 3. Quindi per la mia matematica che esce a 32 terabyte come dimensione massima. Penso che raggiungerai i limiti del tuo file system prima di colpire quello di SQLite!


3
A seconda delle operazioni che stai eseguendo, provando a eliminare 3000 righe in un database sqlite 8G, ci vuole abbastanza tempo per preparare una bella pentola di stampa francese, lol
benjaminz,

4
@benjaminz, devi sbagliare. Se si esegue la cancellazione di 3k righe in una transazione, dovrebbe essere quasi istantaneo. Ho fatto questo errore da solo: l'eliminazione di 10k righe una alla volta ha richiesto 30 minuti. Ma una volta che ho racchiuso tutte le istruzioni di eliminazione in una transazione, ci sono voluti 5 secondi.
mvp,

55

Gran parte del motivo per cui ci sono voluti> 48 ore per eseguire gli inserimenti è a causa dei tuoi indici. È incredibilmente più veloce per:

1 - Elimina tutti gli indici 2 - Esegui tutti gli inserti 3 - Crea nuovamente gli indici


23
Questo è ben noto ... ma per un lungo processo non lascerai cadere periodicamente gli indici per ricostruirli, specialmente quando li interrogherai per fare un lavoro. Questo è l'approccio adottato, tuttavia, quando il sqlite db deve essere ricostruito da zero, gli indici vengono creati dopo aver eseguito tutti gli inserti.
Snazzer,

24
@Snazzer in una situazione simile abbiamo usato una tabella "accumulatore": una volta al giorno, quindi, spostavamo le righe accumulate dalla tabella accumulatore alla tabella principale all'interno di una singola transazione. Ove necessario, una vista si occupava di presentare entrambe le tabelle come un'unica tabella.
CAFxX,

4
Un'altra opzione è quella di mantenere gli indici, ma preordinare i dati in ordine di indice prima di inserirli.
Steven Kryskalla,

1
@StevenKryskalla come si confronta con il rilascio degli indici e la loro ricreazione? Qualche link che conosci di cui hai fatto il benchmark?
mcmillab,

1
@mcmillab Questo è stato anni fa, quindi non ricordo tutti i dettagli o le statistiche del benchmark, ma pensando intuitivamente, l'inserimento di N elementi ordinati casualmente in un indice richiederà tempo O (NlogN), mentre l'inserimento di N elementi ordinati richiederà O (N ) tempo.
Steven Kryskalla,

34

Oltre alla solita raccomandazione:

  1. Indice di caduta per l'inserimento di massa.
  2. Inserimenti / aggiornamenti batch in transazioni di grandi dimensioni.
  3. Ottimizza la cache del buffer / disabilita journal / w PRAGMAs.
  4. Usa una macchina a 64 bit (per poter usare molta cache ™).
  5. [aggiunto luglio 2014] Utilizzare l' espressione di tabella comune (CTE) invece di eseguire più query SQL! Richiede la versione 3.8.3 di SQLite.

Ho imparato quanto segue dalla mia esperienza con SQLite3:

  1. Per la massima velocità di inserimento, non utilizzare lo schema con alcun vincolo di colonna. (Modificare la tabella in seguito, se necessario Non è possibile aggiungere vincoli con ALTER TABLE).
  2. Ottimizza il tuo schema per archiviare ciò di cui hai bisogno. A volte questo significa scomporre le tabelle e / o persino comprimere / trasformare i dati prima di inserirli nel database. Un ottimo esempio è la memorizzazione degli indirizzi IP come numeri interi (lunghi).
  3. Una tabella per file db - per ridurre al minimo i conflitti di blocco. (Utilizzare ATTACH DATABASE se si desidera disporre di un singolo oggetto connessione.
  4. SQLite può archiviare diversi tipi di dati nella stessa colonna (tipizzazione dinamica), utilizzarli a proprio vantaggio.

Domanda / commento benvenuto. ;-)


1
Quanto si ottiene da "una tabella per file db"? Sembra interessante. Pensi che importerebbe molto se il tuo tavolo ha solo 3 tavoli e viene creato da zero?
Martin Velez,

4
@martin odia dirlo, ma la risposta è che dipende . L'idea è di suddividere i dati in dimensioni gestibili. Nel mio caso d'uso raccolgo dati da host diversi e faccio report sui dati dopo il fatto, quindi questo approccio ha funzionato bene. La partizione per data / ora, come suggerito da altri, dovrebbe funzionare bene per i dati che durano a lungo immagino.
Lester Cheung,

3
@Lester Cheung: Per quanto riguarda la tua seconda # 1: è mia comprensione dai documenti e dall'esperienza personale che fino ad oggi SQLite3 non supporta l'aggiunta di vincoli con ALTER TABLE dopo la creazione della tabella. L'unico modo per aggiungere o rimuovere i vincoli dalle righe della tabella esistente è creare una nuova tabella con le caratteristiche desiderate e copiarla su tutte le righe, che è probabilmente molto più lenta dell'inserimento una volta con i vincoli.
Mumbleskates,

3
@Widdershins hai perfettamente ragione - ALTER TABLE in SQLite non consente l'aggiunta di vincoli. Non so cosa stavo fumando - aggiornerò la risposta - grazie.
Lester Cheung,

Nessuno di questi suggerimenti ha nulla a che fare con l'uso di file db SQLite di grandi dimensioni. La domanda è stata modificata da quando è stata inviata questa risposta?
A. Rager,

9

Penso che le principali lamentele sul ridimensionamento di sqlite siano:

  1. Scrittura a processo singolo.
  2. Nessun mirroring.
  3. Nessuna replica.

9

Ho un database SQLite da 7 GB. Per eseguire una query specifica con un join interno sono necessari 2.6 secondi. Per accelerare ciò, ho provato ad aggiungere indici. A seconda dell'indice o degli indici che ho aggiunto, a volte la query è scesa a 0,1 secondi, a volte fino a 7 secondi. Penso che il problema nel mio caso fosse che se una colonna è altamente duplicata, l'aggiunta di un indice peggiora le prestazioni :(


9
Perché una colonna con molti duplicati degraderebbe le prestazioni (domanda seria)?
Martin Velez,

6
una colonna con bassa cardinalità è più difficile da index: stackoverflow.com/questions/2113181/...
Metrix

9

Nella documentazione di SQLite c'era una dichiarazione secondo cui il limite di dimensioni pratiche di un file di database era di alcune decine di GB: s. Ciò è dovuto principalmente alla necessità che SQLite "alloca una bitmap di pagine sporche" ogni volta che si avvia una transazione. Pertanto erano necessari 256 byte di RAM per ogni MB nel database. L'inserimento in un file DB da 50 GB richiederebbe un pesante (2 ^ 8) * (2 ^ 10) = 2 ^ 18 = 256 MB di RAM.

Ma a partire dalle versioni recenti di SQLite, questo non è più necessario. Leggi di più qui .


25
Mi dispiace molto doverlo sottolineare, ma 2^18in realtà è solo 256 K.
Gabriel Schreiber,

7
@GabrielSchreiber che, e anche il fatto che 50 GB non siano (2 ^ 10) MB, sono solo 1 GB. Quindi, per un database da 50 GB, sono necessari 12,5 MB di memoria: (2 ^ 8) * (2 ^ 10) * 50
elipoultorak

8

Ho riscontrato problemi con file sqlite di grandi dimensioni durante l'utilizzo del comando vacuum.

Non ho ancora provato la funzione auto_vacuum. Se ti aspetti di aggiornare ed eliminare spesso i dati, vale la pena esaminarli.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.