362 votes

Quelles sont les caractéristiques de performance de sqlite avec des fichiers de base de données très volumineux ?

Mise à jour 2020 environ 11 ans après que la question ait été postée, puis fermée, ce qui empêche les réponses plus récentes.

Presque tout ce qui est écrit ici est obsolète. Il fut un temps où sqlite était limité à la capacité de la mémoire ou à 2 Go de stockage (32 bits) ou d'autres chiffres populaires... enfin, c'était il y a longtemps.

Les limitations officielles sont énumérées ici . En pratique, sqlite est susceptible de fonctionner tant qu'il y a du stockage disponible. . Il fonctionne bien avec des ensembles de données plus grands que la mémoire, il a été créé à l'origine lorsque la mémoire était mince et c'était un point très important dès le départ.

Il n'y a absolument aucun problème à stocker 100 Go de données. Il pourrait probablement stocker 1 To sans problème, mais c'est à ce moment-là que vous devez vous demander si SQLite est le meilleur outil pour ce travail et vous voulez probablement les fonctionnalités d'une base de données à part entière (clients distants, écritures simultanées, répliques en lecture seule, sharding, etc...).


Original :

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

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

Je parle de fichiers de données sqlite de l'ordre de plusieurs gigaoctets, à partir de 2 Go. Quelqu'un a-t-il de l'expérience dans ce domaine ? Des conseils/idées ?

1 votes

L'utilisation du threading (connexion par thread) ne peut être utile que pour la lecture. stackoverflow.com/a/24029046/743263

2 votes

33 votes

Année 2016 : J'ai une base de données de 5 Go qui fonctionne sur SQLite sans problème. J'ai installé exactement le même jeu de données sur Postgres. SQLite a exécuté une requête complexe en 2,7 ms, Postgres en 2,5 ms. J'ai fini par choisir Postgres pour l'accès plus facile à Regex et les meilleures fonctionnalités d'index. Mais j'ai été impressionné par SQLite et j'aurais pu l'utiliser aussi bien.

260voto

Snazzer Points 2688

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

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

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

Table unique

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 et très grande table sqlite aura des problèmes avec les insertions, et probablement aussi avec d'autres opérations.

Je suppose que ce n'est pas une surprise, car plus la table est grande, plus l'insertion et la mise à jour de tous les indices prennent du temps.

Tables multiples

J'ai ensuite essayé de répartir les données par heure sur plusieurs tableaux, un tableau par jour. Les données de la table originale ont été réparties sur ~700 tables.

Cette configuration n'a posé aucun problème d'insertion, elle n'a pas pris plus de temps au fur et à mesure que le temps passait, puisqu'une nouvelle table était 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. Plus les insertions/suppressions sont nombreuses, plus la fragmentation du fichier sur le disque s'aggrave. Le but est donc de VACUUMer périodiquement pour optimiser le fichier et récupérer de l'espace.

Cependant, comme l'a souligné documentation Une copie complète de la base de données est effectuée pour faire le vide, ce qui prend beaucoup de temps. Donc, plus la base de données est petite, plus cette opération sera rapide.

Conclusions

Pour mon application spécifique, je vais probablement répartir les données sur plusieurs fichiers de base de données, un par jour, afin d'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 autre avantage est que je peux simplement supprimer un fichier db entier pour abandonner une journée de données (une opération courante pour mon application).

Je devrais probablement surveiller la taille des tables par fichier pour savoir quand la vitesse deviendra un problème.

C'est dommage qu'il ne semble pas y avoir de méthode de vide incrémentiel autre que aspirateur automatique . Je ne peux pas l'utiliser parce que mon objectif pour le vide est de défragmenter le fichier (l'espace du fichier n'est pas un problème important), ce que le vide automatique ne fait pas. En fait, la documentation indique qu'il peut aggraver la fragmentation, ce qui m'oblige à faire périodiquement un vide complet sur le fichier.

6 votes

Des informations très utiles. Pure spéculation mais je me demande si la nouvelle API de sauvegarde peut être utilisée pour créer une version non fragmentée de votre base de données sur une base quotidienne, et éviter le besoin d'exécuter un VACUUM.

28 votes

Je suis curieux, est-ce que tous vos INSERTS étaient dans une transaction ?

10 votes

Oui, les insertions étaient faites par lots de 10000 messages par transaction.

184voto

Alex Points 691

Nous utilisons des DBS de plus de 50 Go sur notre plateforme. Aucun problème. Assurez-vous que vous faites tout ce qu'il faut ! Utilisez-vous des instructions prédéfinies ? *SQLITE 3.7.3

  1. Transactions
  2. Déclarations préalables
  3. Appliquez ces paramètres (juste après avoir créé la BD)

    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 d'autres personnes, cela fonctionne très bien ici

23 votes

Récemment testé avec des dbs de l'ordre de 160GB, il fonctionne également très bien.

13 votes

Aussi PRAGMA main.temp_store = MEMORY; .

44 votes

@Alex, pourquoi y a-t-il deux PRAGMA main.cache_size=5000; ?

66voto

Paul Lefebvre Points 3624

J'ai créé des bases de données SQLite d'une taille allant jusqu'à 3,5 Go sans problème de performances notable. Si je me souviens bien, je pense que SQLite2 pouvait avoir des limites plus basses, mais je ne pense pas que SQLite3 ait de tels problèmes.

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

3 votes

Selon les opérations que vous effectuez, si vous essayez de supprimer 3000 lignes dans une base de données sqlite de 8G, cela prend suffisamment de temps pour que vous puissiez préparer un bon pot de presse française, lol.

8 votes

@benjaminz, vous devez faire une erreur. Si vous supprimez 3k lignes en une seule transaction, cela devrait être presque instantané. J'ai moi-même fait cette erreur : la suppression de 10k lignes une par une a pris 30 minutes. Mais une fois que j'ai regroupé toutes les déclarations de suppression dans une transaction, cela a pris 5s.

61voto

user352992 Points 201

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

1 - Abandonner tous les index 2 - Faites toutes les insertions 3 - Créez à nouveau des index

23 votes

C'est bien connu... mais pour un processus de longue durée, vous n'allez pas abandonner périodiquement vos index pour les reconstruire, surtout si vous allez les interroger pour faire votre travail. C'est pourtant l'approche adoptée lorsque la base de données sqlite doit être reconstruite à partir de zéro, les index sont créés après que toutes les insertions ont été effectuées.

26 votes

@Snazzer Dans une situation similaire, nous utilisions une table "accumulatrice" : une fois par jour, nous déplacions les lignes accumulées de la table accumulatrice vers la table principale dans une seule transaction. Si nécessaire, une vue se chargeait de présenter les deux tables comme une seule table.

4 votes

Une autre option consiste à conserver les index, mais à trier les données dans l'ordre des index avant de les insérer.

36voto

Lester Cheung Points 597

En plus de la recommandation habituelle :

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

Mon expérience avec SQLite3 m'a appris ce qui suit :

  1. Pour une vitesse d'insertion maximale, n'utilisez pas de schéma avec une contrainte de colonne. ( Modifiez le tableau ultérieurement 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. Cela signifie parfois qu'il faut 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 le stockage des adresses IP sous forme d'entiers (longs).
  3. Une table par fichier db - pour minimiser la contention des verrous. (Utilisez JOINDRE LA BASE DE DONNÉES si vous voulez avoir un seul objet de connexion.
  4. SQLite peut stocker différents types de données dans la même colonne (typage dynamique), utilisez cela à votre avantage.

Questions/commentaires bienvenus ;-)

1 votes

Quel est l'impact d'une table par fichier DB ? Cela semble intéressant. Pensez-vous que cela aurait beaucoup d'importance si votre table ne compte que 3 tables et qu'elle est construite à partir de zéro ?

4 votes

@martin je déteste dire ça mais la réponse est cela dépend . L'idée est de partitionner les données à une taille gérable. Dans mon cas d'utilisation, je recueille des données à partir de différents hôtes et je fais des rapports sur les données après coup, donc cette approche a bien fonctionné. Le partitionnement par date/heure, tel que suggéré par d'autres, devrait bien fonctionner pour les données qui couvrent une longue période de temps, j'imagine.

3 votes

@Lester Cheung : En ce qui concerne votre second point #1 : D'après ce que je comprends de la documentation et de mon expérience personnelle, à ce jour, SQLite3 ne supporte pas 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 d'une table existante est de créer une nouvelle table avec les caractéristiques souhaitées et de copier sur toutes les lignes, ce qui est susceptible d'être beaucoup plus lent que d'insérer une fois avec des contraintes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X