Quelles sont les caractéristiques de performances de sqlite avec des fichiers de base de données très volumineux? [fermé]

325

Je sais que sqlite ne fonctionne pas bien avec des fichiers de base de données extrêmement volumineux même lorsqu'ils sont pris en charge (il y avait un commentaire sur le site Web sqlite indiquant que si vous avez besoin de tailles de fichier supérieures à 1 Go, vous pouvez envisager d'utiliser un rdbms d'entreprise. ne le trouve plus, peut être lié à une ancienne version de sqlite).

Cependant, pour mes besoins, j'aimerais avoir une idée de la gravité de la situation avant d'envisager d'autres solutions.

Je parle de fichiers de données sqlite dans la plage de plusieurs gigaoctets, à partir de 2 Go. Quelqu'un a-t-il une expérience avec ceci? Des conseils / idées?

Snazzer
la source
1
L'utilisation du filetage (connexion par fil) peut aider uniquement à la lecture - stackoverflow.com/a/24029046/743263
malkia
23
Année 2016: J'ai une base de données de 5 Go qui fonctionne sur SQLite sans aucun problème. J'ai installé exactement le même ensemble de données sur Postgres. SQLite a exécuté une requête complexe en 2,7 ms, Postgres en 2,5 ms. Je me suis retrouvé sur Postgres pour un accès Regex plus facile et de meilleures fonctionnalités d'index. Mais j'ai été impressionné par SQLite et aurais pu aussi l'utiliser.
Paulb

Réponses:

246

J'ai donc fait quelques tests avec sqlite pour les très gros fichiers, et je suis parvenu à des conclusions (au moins pour mon application spécifique).

Les tests impliquent un seul fichier sqlite avec une seule table ou plusieurs tables. Chaque table avait environ 8 colonnes, presque tous des entiers et 4 indices.

L'idée était d'insérer suffisamment de données jusqu'à ce que les fichiers sqlite mesurent environ 50 Go.

Table simple

J'ai essayé d'insérer plusieurs lignes dans un fichier sqlite avec une seule table. Lorsque le fichier était d'environ 7 Go (désolé, je ne peux pas être précis sur le nombre de lignes), les insertions prenaient beaucoup trop de temps. J'avais estimé que mon test pour insérer toutes mes données prendrait environ 24 heures, mais il ne s'est pas terminé même après 48 heures.

Cela m'amène à conclure qu'une seule table sqlite très grande aura des problèmes avec les insertions, et probablement d'autres opérations également.

Je suppose que ce n'est pas une surprise, car le tableau s'agrandit, l'insertion et la mise à jour de tous les indices prennent plus de temps.

Tables multiples

J'ai ensuite essayé de diviser les données par le temps sur plusieurs tables, une table par jour. Les données du tableau 1 d'origine ont été divisées en ~ 700 tableaux.

Cette configuration n'a eu aucun problème avec l'insertion, elle n'a pas pris plus de temps avec le temps, car une nouvelle table a été créée pour chaque jour.

Problèmes de vide

Comme l'a souligné i_like_caffeine, la commande VACUUM est un problème plus le fichier sqlite est volumineux. Au fur et à mesure que plus d'insertions / suppressions sont effectuées, la fragmentation du fichier sur le disque va s'aggraver, donc l'objectif est de périodiquement VACUUM pour optimiser le fichier et récupérer l'espace du fichier.

Cependant, comme le souligne la documentation , une copie complète de la base de données est réalisée pour faire le vide, ce qui prend beaucoup de temps. Ainsi, plus la base de données est petite, plus cette opération se terminera rapidement.

Conclusions

Pour mon application spécifique, je répartirai probablement les données sur plusieurs fichiers db, un par jour, pour obtenir le meilleur des performances de vide et de la vitesse d'insertion / suppression.

Cela complique les requêtes, mais pour moi, c'est un compromis intéressant de pouvoir indexer autant de données. Un avantage supplémentaire est que je peux simplement supprimer un fichier db entier pour supprimer une journée de données (une opération courante pour mon application).

Je devrais probablement également surveiller la taille de la table par fichier pour voir quand la vitesse deviendra un problème.

Il est dommage qu'il ne semble pas y avoir de méthode de vide incrémentielle autre que le vide automatique . Je ne peux pas l'utiliser car mon objectif pour le vide est de défragmenter le fichier (l'espace fichier n'est pas un gros problème), ce que le vide automatique ne fait pas. En fait, la documentation indique que cela peut aggraver la fragmentation, je dois donc recourir périodiquement à un vide complet sur le fichier.

Snazzer
la source
5
Info très utile. Pure spéculation mais je me demande si la nouvelle API de sauvegarde peut être utilisée pour créer quotidiennement une version non fragmentée de votre base de données et éviter d'avoir à exécuter un VIDE.
eodonohoe
24
Je suis curieux, tous vos INSERTS étaient-ils dans une transaction?
Paul Lefebvre
9
Oui, les insertions ont été effectuées par lots de 10 000 messages par transaction.
Snazzer
6
Quel système de fichiers avez-vous utilisé? Si ext {2,3,4}, quel était le paramètre data =, la journalisation était-elle activée? Outre les modèles io, la façon dont sqlite se vide sur le disque peut être importante.
Tobu
5
Je testais principalement sur Windows, donc je ne peux pas commenter le comportement sur Linux.
Snazzer
169

Nous utilisons DBS de 50 Go + sur notre plateforme. aucune plainte fonctionne très bien. Assurez-vous que vous faites tout correctement! Utilisez-vous des instructions prédéfinies? * SQLITE 3.7.3

  1. Transactions
  2. Déclarations pré-faites
  3. Appliquer ces paramètres (juste après avoir créé la base de données)

    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;
    

J'espère que cela aidera les autres, fonctionne très bien ici

Alex
la source
22
Récemment testé avec dbs dans la gamme 160 Go, fonctionne très bien également.
Snazzer
10
Aussi PRAGMA main.temp_store = MEMORY;.
Vikrant Chaudhary
40
@Alex, pourquoi il y a deux PRAGMA main.cache_size = 5000 ;?
Jack
23
Ne vous contentez pas d'appliquer aveuglément ces optimisations. En particulier synchrone = NORMAL n'est pas protégé contre les pannes. C'est-à-dire qu'un crash de processus au bon moment peut corrompre votre base de données même en l'absence de pannes de disque. sqlite.org/pragma.html#pragma_synchronous
mpm
22
@Alex pouvez-vous expliquer ces valeurs et la différence entre elles et celles par défaut?
4m1nh4j1
65

J'ai créé des bases de données SQLite d'une taille maximale de 3,5 Go sans problèmes de performances notables. Si je me souviens bien, je pense que SQLite2 aurait pu avoir des limites inférieures, mais je ne pense pas que SQLite3 ait de tels problèmes.

Selon la page SQLite Limits , la taille maximale de chaque page de base de données est de 32 Ko. Et le nombre maximum de pages dans une base de données est de 1024 ^ 3. Donc, d'après mes calculs, cela fait 32 téraoctets comme taille maximale. Je pense que vous atteindrez les limites de votre système de fichiers avant de toucher à SQLite!

Paul Lefebvre
la source
3
Selon les opérations que vous effectuez, en essayant de supprimer 3000 lignes dans une base de données sqlite 8G, il vous faut suffisamment de temps pour préparer un joli pot de presse française, lol
benjaminz
4
@benjaminz, vous devez vous tromper. Si vous encapsulez la suppression de 3 000 lignes en une seule transaction, cela devrait être presque instantané. J'ai eu cette erreur moi-même: la suppression de 10 000 lignes une par une a pris 30 minutes. Mais une fois que j'ai enveloppé toutes les instructions de suppression dans une seule transaction, cela a pris 5 secondes.
mvp
55

Une grande partie de la raison pour laquelle il a fallu plus de 48 heures pour faire vos insertions est à cause de vos index. Il est incroyablement plus rapide de:

1 - Supprimer tous les index 2 - Faire toutes les insertions 3 - Créer à nouveau des index

user352992
la source
23
C'est bien connu ... mais pour un long processus, vous n'allez pas supprimer régulièrement vos index pour les reconstruire, surtout lorsque vous allez les interroger pour faire du travail. C'est l'approche adoptée, mais lorsque la base de données sqlite doit être reconstruite à partir de zéro, les index sont créés après toutes les insertions.
Snazzer
24
@Snazzer dans une situation similaire, nous avons utilisé une table "accumulateur": une fois par jour, nous déplacions ensuite les lignes accumulées de la table accumulateur vers la table principale au sein d'une seule transaction. Au besoin, une vue s'est occupée de présenter les deux tables comme une seule table.
CAFxX
4
Une autre option consiste à conserver les index, mais à pré-trier les données dans l'ordre des index avant de les insérer.
Steven Kryskalla
1
@StevenKryskalla comment cela se compare-t-il à la suppression des index et à leur recréation? Des liens que vous connaissez et qui ont été comparés?
mcmillab
1
@mcmillab C'était il y a des années donc je ne me souviens pas de tous les détails ni des statistiques de référence, mais en pensant intuitivement, l'insertion de N éléments ordonnés de façon aléatoire dans un index prendra O (NlogN), tandis que l'insertion de N éléments triés prendra O (N ) temps.
Steven Kryskalla
34

Outre la recommandation habituelle:

  1. Indice de chute pour l'insertion en vrac.
  2. Inserts / mises à jour par lots dans les transactions importantes.
  3. Réglez votre cache tampon / désactivez le journal / w PRAGMA.
  4. Utilisez une machine 64 bits (pour pouvoir utiliser beaucoup de cache ™).
  5. [ajouté en juillet 2014] Utilisez l'expression de table commune (CTE) au lieu d'exécuter plusieurs requêtes SQL! Nécessite la version 3.8.3 de SQLite.

J'ai appris ce qui suit de mon expérience avec SQLite3:

  1. Pour une vitesse d'insertion maximale, n'utilisez pas de schéma avec une contrainte de colonne. (Modifier la table plus tard si nécessaire Vous ne pouvez pas ajouter de contraintes avec ALTER TABLE).
  2. Optimisez votre schéma pour stocker ce dont vous avez besoin. Parfois, cela signifie décomposer les tables et / ou même compresser / transformer vos données avant de les insérer dans la base de données. Un bon exemple est de stocker les adresses IP sous forme d'entiers (longs).
  3. Une table par fichier db - pour minimiser les conflits de verrouillage. (Utilisez ATTACH DATABASE si vous souhaitez avoir un seul objet de connexion.
  4. SQLite peut stocker différents types de données dans la même colonne (typage dynamique), utilisez-les à votre avantage.

Bienvenue question / commentaire. ;-)

Lester Cheung
la source
1
Quel impact tirez-vous d'une «table par fichier db»? Ça a l'air intéressant. Pensez-vous que cela aurait beaucoup d'importance si votre table n'a que 3 tables et est construite à partir de zéro?
Martin Velez
4
@martin déteste le dire, mais la réponse est que cela dépend . L'idée est de partitionner les données à une taille gérable. Dans mon cas d'utilisation, je collecte des données auprès de différents hôtes et je fais des rapports sur les données après coup, donc cette approche a bien fonctionné. La partition par date / heure, comme suggéré par d'autres, devrait bien fonctionner pour les données qui s'étendent sur une longue période, j'imagine.
Lester Cheung
3
@Lester Cheung: Concernant votre deuxième # 1: d'après ma documentation et mon expérience personnelle, à ce jour, SQLite3 ne prend pas en charge l'ajout de contraintes avec ALTER TABLE après la création de la table. La seule façon d'ajouter ou de supprimer des contraintes des lignes de table existantes consiste à créer une nouvelle table avec les caractéristiques souhaitées et à copier sur toutes les lignes, ce qui est probablement beaucoup plus lent que l'insertion d'une seule fois avec des contraintes.
Mumbleskates
3
@Widdershins vous avez absolument raison - ALTER TABLE dans SQLite ne permet pas d'ajouter des contraintes. Je ne sais pas ce que je fumais - mettra à jour la réponse - merci.
Lester Cheung
Aucune de ces suggestions n'a rien à voir avec l'utilisation de gigantesques fichiers db SQLite. La question a-t-elle été modifiée depuis la soumission de cette réponse?
A. Rager
9

Je pense que les principales plaintes concernant la mise à l'échelle sqlite sont les suivantes:

  1. Écriture de processus unique.
  2. Pas de miroir.
  3. Pas de réplication.
Inconnue
la source
9

J'ai une base de données SQLite de 7 Go. Pour effectuer une requête particulière avec une jointure interne, il faut 2,6 secondes. Pour accélérer cela, j'ai essayé d'ajouter des index. Selon le ou les index que j'ai ajoutés, parfois la requête descendait à 0,1 s et parfois elle montait jusqu'à 7 s. Je pense que le problème dans mon cas était que si une colonne est très en double, l'ajout d'un index dégrade les performances :(

Mike Oxynormas
la source
9
Pourquoi une colonne avec de nombreux doublons dégraderait-elle les performances (question sérieuse)?
Martin Velez
6
une colonne avec une faible cardinalité est plus difficile à indexer: stackoverflow.com/questions/2113181/…
metrix
9

Il y avait auparavant une déclaration dans la documentation SQLite selon laquelle la limite de taille pratique d'un fichier de base de données était de quelques dizaines de Go: s. Cela était principalement dû à la nécessité pour SQLite «d'allouer un bitmap de pages sales» chaque fois que vous avez commencé une transaction. Ainsi, 256 octets de RAM étaient nécessaires pour chaque Mo de la base de données. L'insertion dans un fichier DB de 50 Go nécessiterait un lourd (2 ^ 8) * (2 ^ 10) = 2 ^ 18 = 256 Mo de RAM.

Mais depuis les versions récentes de SQLite, cela n'est plus nécessaire. Lisez plus ici .

Alix Axel
la source
25
Je suis vraiment désolé de devoir le signaler, mais il ne 2^18s'agit en fait que de 256 K.
Gabriel Schreiber
7
@GabrielSchreiber cela, et aussi le fait que 50 Go n'est pas (2 ^ 10) Mo, c'est seulement 1 Go. Donc, pour une base de données de 50 Go, vous avez besoin de 12,5 Mo de mémoire: (2 ^ 8) * (2 ^ 10) * 50
elipoultorak
8

J'ai rencontré des problèmes avec de gros fichiers sqlite lors de l'utilisation de la commande vacuum.

Je n'ai pas encore essayé la fonction auto_vacuum. Si vous prévoyez de mettre à jour et de supprimer des données souvent, cela vaut la peine d'être examiné.

eodonohoe
la source