Accès simultané à SQLite

177

SQLite3 gère-t-il en toute sécurité l'accès simultané par plusieurs processus lisant / écrivant à partir de la même base de données? Y a-t-il des exceptions de plate-forme à cela?

anand
la source
3
J'ai oublié de mentionner le but de la prime : la plupart des réponses disent que c'est ok: "SQLite est assez rapide", "SQLite gère bien la concurrence" etc. mais, à mon humble avis, ne répondez pas en détail / n'expliquez pas clairement ce qui se passe si deux opérations d'écriture arriverait exactement au même moment (cas théorique très rare). 1) Cela déclencherait-il une erreur et interromprait-il le programme? ou 2) La deuxième opération d'écriture attendrait-elle la fin de la première? ou 3) Une des opérations d'écriture serait-elle rejetée (perte de données!)? 4) Quelque chose d'autre? Connaître les limites de l'écriture simultanée peut être utile dans de nombreuses situations.
Basj
7
@Basj En bref, 2) il va attendre et réessayer plusieurs fois (configurable), 1) déclencher une erreur, SQLITE_BUSY.3) vous pouvez enregistrer un rappel pour gérer les erreurs SQLITE_BUSY.
obgnaw

Réponses:

112

Si la plupart de ces accès simultanés sont des lectures (par exemple SELECT), SQLite peut très bien les gérer. Mais si vous commencez à écrire simultanément, les conflits de verrouillage peuvent devenir un problème. Beaucoup dépendrait alors de la vitesse de votre système de fichiers, car le moteur SQLite lui-même est extrêmement rapide et dispose de nombreuses optimisations intelligentes pour minimiser les conflits. Surtout SQLite 3.

Pour la plupart des applications de bureau / ordinateur portable / tablette / téléphone, SQLite est assez rapide car il n'y a pas assez de concurrence. (Firefox utilise largement SQLite pour les signets, l'historique, etc.)

Pour les applications serveur, quelqu'un a dit il y a quelque temps que moins de 100K pages vues par jour pouvaient être parfaitement gérées par une base de données SQLite dans des scénarios typiques (par exemple, des blogs, des forums), et je n'ai pas encore vu de preuves du contraire. En fait, avec les disques et les processeurs modernes, 95% des sites Web et des services Web fonctionneraient parfaitement avec SQLite.

Si vous voulez un accès en lecture / écriture très rapide, utilisez une base de données SQLite en mémoire . La RAM est de plusieurs ordres de grandeur plus rapide que le disque.

Kijin
la source
16
OP ne pose pas de questions sur l'efficacité et la vitesse, mais sur l'accès simultané. Les serveurs Web n'ont rien à voir avec cela. Idem dans la base de données mémoire.
Jarekczek
1
Vous avez raison dans une certaine mesure, mais l'efficacité / la rapidité jouent un rôle. Des accès plus rapides signifient que le temps passé à attendre les verrous est plus court, réduisant ainsi les inconvénients des performances de concurrence de SQLite. En particulier, si vous avez peu d'écritures rapides, la base de données ne semblera pas du tout avoir de problèmes de concurrence pour un utilisateur.
Caboose
1
comment géreriez-vous l'accès simultané à une base de données sqlite en mémoire?
P-Gn
42

Oui, SQLite gère bien la concurrence, mais ce n'est pas le meilleur du point de vue des performances. D'après ce que je peux dire, il n'y a aucune exception à cela. Les détails sont sur le site de SQLite: https://www.sqlite.org/lockingv3.html

Cette déclaration est intéressante: "Le module de téléavertisseur s'assure que les modifications se produisent en une seule fois, que toutes les modifications se produisent ou qu'aucune d'entre elles ne se produisent, que deux processus ou plus n'essaient pas d'accéder à la base de données de manière incompatible en même temps"

vcsjones
la source
2
Voici quelques commentaires sur les problèmes rencontrés sur différentes plates - formes , à savoir les systèmes de fichiers NFS et Windows (bien que cela puisse concerner uniquement les anciennes versions de Windows ...)
Nate
1
Est-il possible de charger une base de données SQLite3 dans la RAM pour être utilisée par tous les utilisateurs en PHP? Je suppose que non, c'est procédural
Anon343224user
1
@foxyfennec .. un point de départ, bien que SQLite ne soit pas la base de données optimale pour ce cas d'utilisation. sqlite.org/inmemorydb.html
kingPuppy
37

Oui. Voyons pourquoi

SQLite est transactionnel

Toutes les modifications au sein d'une même transaction dans SQLite se produisent complètement ou pas du tout

Un tel support ACID ainsi que des lectures / écritures simultanées sont fournis de 2 manières - en utilisant la soi-disant journalisation (appelons-la « ancienne méthode ») ou la journalisation en écriture anticipée (appelons-la « nouvelle manière »)

Journalisation (ancienne méthode)

Dans ce mode, SQLite utilise le verrouillage DATABASE-LEVEL . C'est le point crucial à comprendre.

Cela signifie que chaque fois qu'il a besoin de lire / écrire quelque chose, il acquiert d'abord un verrou sur le fichier de base de données ENTIER . Plusieurs lecteurs peuvent coexister et lire quelque chose en parallèle

Pendant l'écriture, il s'assure qu'un verrou exclusif est acquis et qu'aucun autre processus ne lit / écrit simultanément et que les écritures sont donc sûres.

C'est pourquoi ici ils disent que SQlite implémente des transactions sérialisables

Troubles

Comme il doit verrouiller une base de données entière à chaque fois et que tout le monde attend un processus de gestion de l'écriture, la concurrence en souffre et ces écritures / lectures simultanées sont de performances assez faibles

Annulations / pannes

Avant d'écrire quelque chose dans le fichier de base de données, SQLite enregistre d'abord le bloc à modifier dans un fichier temporaire. Si quelque chose se bloque au milieu de l'écriture dans le fichier de base de données, il récupérera ce fichier temporaire et annulera les modifications.

Journalisation à écriture anticipée ou WAL (nouvelle méthode)

Dans ce cas, toutes les écritures sont ajoutées à un fichier temporaire ( journal des écritures anticipées ) et ce fichier est périodiquement fusionné avec la base de données d'origine. Lorsque SQLite recherche quelque chose, il vérifie d'abord ce fichier temporaire et si rien n'est trouvé, continuez avec le fichier de base de données principal.

En conséquence, les lecteurs ne sont pas en concurrence avec les écrivains et les performances sont bien meilleures par rapport à l'ancienne.

Mises en garde

SQlite dépend fortement de la fonctionnalité de verrouillage du système de fichiers sous-jacent, il doit donc être utilisé avec prudence, plus de détails ici

Vous êtes également susceptible de rencontrer une erreur de verrouillage de la base de données , en particulier en mode journalisé, votre application doit donc être conçue avec cette erreur à l'esprit.

fête
la source
35

Personne ne semble avoir mentionné le mode WAL (Write Ahead Log). Assurez-vous que les transactions sont correctement organisées et avec le mode WAL activé, il n'est pas nécessaire de garder la base de données verrouillée pendant que les gens lisent des choses pendant qu'une mise à jour est en cours.

Le seul problème est qu'à un moment donné, le WAL doit être réincorporé dans la base de données principale, et il le fait lorsque la dernière connexion à la base de données se ferme. Avec un site très occupé, vous constaterez peut-être que la fermeture de toutes les connexions prend quelques secondes, mais 100 000 visites par jour ne devraient pas être un problème.

akc42
la source
Intéressant, mais ne fonctionne que sur une seule machine, pas sur des scénarios où la base de données est accessible sur le réseau.
Bobík
Il vaut la peine de mentionner que le délai d'attente par défaut pour un écrivain est de 5 secondes et après cette database is lockederreur sera levée par l'auteur
mirhossein
16

En 2019, deux nouvelles options d'écriture simultanée ne sont pas encore publiées mais disponibles dans des succursales distinctes.

"PRAGMA journal_mode = wal2"

L'avantage de ce mode journal par rapport au mode "wal" normal est que les rédacteurs peuvent continuer à écrire dans un fichier wal pendant que l'autre est contrôlé.

BEGIN CONCURRENT - lien vers la documentation détaillée

L'amélioration BEGIN CONCURRENT permet à plusieurs rédacteurs de traiter simultanément les transactions d'écriture si la base de données est en mode «wal» ou «wal2», bien que le système sérialise toujours les commandes COMMIT.

Lorsqu'une transaction d'écriture est ouverte avec "BEGIN CONCURRENT", le verrouillage réel de la base de données est différé jusqu'à ce qu'un COMMIT soit exécuté. Cela signifie que n'importe quel nombre de transactions démarrées avec BEGIN CONCURRENT peut se dérouler simultanément. Le système utilise un verrouillage optimiste au niveau de la page pour empêcher la validation de transactions simultanées conflictuelles.

Ensemble, ils sont présents dans begin-concurrent-wal2 ou chacun dans une branche distincte .

VB
la source
1
Avons-nous une idée de la date à laquelle ces fonctionnalités seront intégrées à la version finale? Ils pourraient vraiment être utiles pour moi.
Peter Moore
2
Aucune idée. Vous pouvez construire facilement à partir des branches. Pour .NET, j'ai une bibliothèque avec une interface de bas niveau & WAL2 + begin concurrent + FTS5: github.com/Spreads/Spreads.SQLite
VB
Oh, bien sûr merci. Je m'interroge davantage sur la stabilité. SQLite est assez haut de gamme en ce qui concerne leurs versions, mais je ne sais pas à quel point il serait risqué d'utiliser une branche dans le code de production.
Peter Moore
2
Voir ce fil github.com/Expensify/Bedrock/issues/65 et Bedrock en général. Ils l'utilisent en production et ont poussé ce begin concurrenttruc.
VB
13

SQLite a un verrou de lecture-écriture au niveau de la base de données. Plusieurs connexions (possiblement détenues par différents processus) peuvent lire les données de la même base de données en même temps, mais une seule peut écrire dans la base de données.

SQLite prend en charge un nombre illimité de lecteurs simultanés, mais il n'autorisera qu'un seul écrivain à tout instant dans le temps. Dans de nombreuses situations, ce n'est pas un problème. Écrivain file d'attente. Chaque application fait son travail de base de données rapidement et évolue, et aucun verrou ne dure plus de quelques dizaines de millisecondes. Mais certaines applications nécessitent plus de concurrence et ces applications peuvent avoir besoin de rechercher une solution différente. - Utilisations appropriées pour SQLite @ SQLite.org

Le verrou lecteurs-rédacteurs permet un traitement indépendant des transactions et est implémenté à l'aide de verrous exclusifs et partagés au niveau de la base de données.

Un verrou exclusif doit être obtenu avant qu'une connexion effectue une opération d'écriture sur une base de données. Une fois le verrou exclusif obtenu, les opérations de lecture et d'écriture à partir d'autres connexions sont bloquées jusqu'à ce que le verrou soit à nouveau libéré.

Détails d'implémentation pour le cas d'écritures simultanées

SQLite a une table de verrouillage qui permet de verrouiller la base de données le plus tard possible lors d'une opération d'écriture pour assurer une concurrence maximale.

L'état initial est DÉVERROUILLÉ et, dans cet état, la connexion n'a pas encore accédé à la base de données. Lorsqu'un processus est connecté à une base de données et même qu'une transaction a été lancée avec BEGIN, la connexion est toujours à l'état DÉVERROUILLÉ.

Après l'état UNLOCKED, l'état suivant est l'état SHARED. Afin de pouvoir lire (et non écrire) les données de la base de données, la connexion doit d'abord entrer dans l'état SHARED, en obtenant un verrou SHARED. Plusieurs connexions peuvent obtenir et maintenir des verrous SHARED en même temps, de sorte que plusieurs connexions peuvent lire les données de la même base de données en même temps. Mais tant que même un seul verrou SHARED n'est pas libéré, aucune connexion ne peut réussir une écriture dans la base de données.

Si une connexion veut écrire dans la base de données, elle doit d'abord obtenir un verrou RÉSERVÉ.

Un seul verrou RÉSERVÉ peut être actif à la fois, bien que plusieurs verrous PARTAGÉS puissent coexister avec un seul verrou RÉSERVÉ. RESERVED diffère de PENDING en ce que de nouvelles serrures PARTAGÉES peuvent être acquises pendant qu'il y a une serrure RESERVED. - Verrouillage de fichiers et concurrence dans SQLite Version 3 @ SQLite.org

Une fois qu'une connexion obtient un verrou RÉSERVÉ, elle peut commencer à traiter les opérations de modification de la base de données, bien que ces modifications ne puissent être effectuées que dans la mémoire tampon, plutôt que réellement écrites sur le disque. Les modifications apportées au contenu de lecture sont enregistrées dans la mémoire tampon. Lorsqu'une connexion souhaite soumettre une modification (ou une transaction), il est nécessaire de mettre à niveau le verrou RÉSERVÉ vers un verrou EXCLUSIF. Pour obtenir la serrure, vous devez d'abord soulever la serrure jusqu'à une serrure PENDING.

Un verrou EN ATTENTE signifie que le processus qui détient le verrou veut écrire dans la base de données dès que possible et attend juste que tous les verrous SHARED actuels soient effacés afin de pouvoir obtenir un verrou EXCLUSIF. Aucun nouveau verrou SHARED n'est autorisé sur la base de données si un verrou PENDING est actif, bien que les verrous SHARED existants soient autorisés à continuer.

Un verrou EXCLUSIF est nécessaire pour écrire dans le fichier de base de données. Un seul verrou EXCLUSIF est autorisé sur le fichier et aucun autre verrou de quelque type que ce soit n'est autorisé à coexister avec un verrou EXCLUSIF. Afin de maximiser la concurrence, SQLite s'efforce de minimiser la durée pendant laquelle les verrous EXCLUSIFS sont maintenus. - Verrouillage de fichiers et concurrence dans SQLite Version 3 @ SQLite.org

Vous pourriez donc dire que SQLite gère en toute sécurité l'accès simultané par plusieurs processus écrivant dans la même base de données simplement parce qu'il ne le prend pas en charge! Vous obtiendrez SQLITE_BUSYou SQLITE_LOCKEDpour le deuxième rédacteur lorsqu'il atteindra la limite de relance.

obgnaw
la source
Je vous remercie. Un exemple de code avec 2 écrivains serait super pour comprendre comment cela fonctionne.
Basj
2
@Basj en bref, sqlite a un verrou en lecture-écriture sur le fichier de base de données, identique à l'écriture simultanée d'un fichier. Et avec WAL, l'écriture simultanée ne peut toujours pas être effectuée, mais WAL peut accélérer l'écriture, et la lecture et l'écriture peuvent être simultanées.Et WAL introduit un nouveau verrou comme WAL_READ_LOCK, WAL_WRITE_LOCK.
obgnaw
2
Pourriez-vous utiliser une file d'attente et faire en sorte que plusieurs threads alimentent la file d'attente et qu'un seul thread écrive dans la base de données à l'aide des instructions SQL de la file d'attente. Quelque chose comme ça
Gabriel
Pour WAL - voir sqlite.org/wal.html
Todd
7

Ce fil est vieux mais je pense qu'il serait bon de partager le résultat de mes tests effectués sur sqlite: j'ai exécuté 2 instances de programme python (différents processus même programme) exécutant des instructions SELECT et UPDATE commandes sql dans une transaction avec verrouillage EXCLUSIF et délai d'expiration défini sur 10 secondes pour obtenir un verrou et le résultat était frustrant. Chaque instance a fait une boucle de 10000 pas:

  • se connecter à db avec verrouillage exclusif
  • sélectionner sur une ligne pour lire le compteur
  • mettre à jour la ligne avec une nouvelle valeur égale au compteur incrémenté de 1
  • fermer la connexion à db

Même si sqlite accordait un verrouillage exclusif sur la transaction, le nombre total de cycles réellement exécutés n'était pas égal à 20 000 mais inférieur (nombre total d'itérations sur un seul compteur compté pour les deux processus). Le programme Python n'a presque pas levé une seule exception (une seule fois lors de la sélection pour 20 exécutions). La révision de sqlite au moment du test était 3.6.20 et python v3.3 CentOS 6.5. À mon avis, il est préférable de trouver un produit plus fiable pour ce type de travail ou de restreindre les écritures sur sqlite à un seul processus / thread unique.

asf las
la source
5
Il semble que vous deviez dire des mots magiques pour obtenir un verrou en python, comme discuté ici: stackoverflow.com/a/12848059/1048959 Ceci malgré le fait que la documentation python sqlite vous laisse croire que with conc'est suffisant.
Dan Stahlke