SQLite Concurrent Access

SQLite3 gestisce in modo sicuro l’accesso simultaneo da più processi di lettura / scrittura dallo stesso DB? Esistono eccezioni alla piattaforma?

Se la maggior parte degli accessi concorrenti sono letti (ad esempio SELECT), SQLite può gestirli molto bene. Ma se inizi a scrivere contemporaneamente, il conflitto potrebbe diventare un problema. Molto dipenderebbe poi dalla velocità del proprio filesystem, dal momento che il motore SQLite stesso è estremamente veloce e ha molte ottimizzazioni intelligenti per ridurre al minimo la contesa. Soprattutto SQLite 3.

Per la maggior parte delle applicazioni desktop / laptop / tablet / phone, SQLite è abbastanza veloce in quanto non c’è abbastanza concorrenza. (Firefox utilizza ampiamente SQLite per segnalibri, cronologia, ecc.)

Per le applicazioni server, qualcuno qualche tempo fa ha affermato che qualsiasi cosa di meno di 100K pagine viste al giorno potrebbe essere gestita perfettamente da un database SQLite in scenari tipici (ad esempio blog, forum), e non ho ancora visto alcuna prova contraria. Infatti, con dischi e processori moderni, il 95% dei siti Web e dei servizi Web funzionerebbe perfettamente con SQLite.

Se si desidera un accesso in lettura / scrittura veramente veloce, utilizzare un database SQLite in memoria. La RAM è di diversi ordini di grandezza più veloce del disco.

Sì, SQLite gestisce bene la concorrenza, ma non è il migliore da un angolo di prestazione. Da quello che posso dire, non ci sono eccezioni a questo. I dettagli sono sul sito di SQLite: https://www.sqlite.org/lockingv3.html

Questa affermazione è interessante: “Il modulo cercapersone fa in modo che i cambiamenti avvengano tutti in una volta, ovvero che tutte le modifiche si verificano o nessuna di esse, che due o più processi non provano ad accedere al database in modi incompatibili allo stesso tempo”

Nessuno sembra aver menzionato la modalità WAL (Write Ahead Log). Assicurati che le transazioni siano organizzate correttamente e con la modalità WAL triggersta, non è necessario mantenere il database bloccato mentre le persone leggono le cose mentre è in corso un aggiornamento.

L’unico problema è che a un certo punto il WAL deve essere re-incorporato nel database principale, e lo fa quando l’ultima connessione al database si chiude. Con un sito molto impegnato potresti trovare alcuni secondi in cui tutte le connessioni saranno chiuse, ma non dovrebbero esserci problemi con 100.000 contatti al giorno.

Sì, lo fa. Scopriamo perché

SQLite è transazionale

Tutte le modifiche all’interno di una singola transazione in SQLite si verificano completamente o per niente

Il supporto ACID e le letture / scritture contemporanee sono fornite in due modi: utilizzando il cosiddetto journaling (chiamiamolo ” old way “) o il write-ahead logging (chiamiamolo ” new way “)

Journaling (Old Way)

In questa modalità, SQLite utilizza il blocco DATABASE-LEVEL . Questo è il punto cruciale da capire.

Ciò significa che ogni volta che ha bisogno di leggere / scrivere qualcosa, prima acquisisce un blocco sul file di database INTERO . Più lettori possono coesistere e leggere qualcosa in parallelo

Durante la scrittura si assicura che venga acquisito un blocco esclusivo e nessun altro processo sta leggendo / scrivendo contemporaneamente e quindi le scritture sono sicure.

Questo è il motivo per cui qui stanno dicendo che SQlite implementa transazioni serializzabili

Troubles

Poiché ha bisogno di bloccare un intero database ogni volta e tutti sono in attesa di una gestione del processo, la scrittura della concorrenza soffre e tali scritture / letture concorrenti hanno prestazioni piuttosto basse

Rollback / interruzioni

Prima di scrivere qualcosa nel file di database, SQLite dovrebbe prima salvare il blocco da modificare in un file temporaneo. Se qualcosa si schianta nel mezzo della scrittura nel file di database, raccoglie questo file temporaneo e ne ripristina le modifiche

Registrazione in scrittura o WAL (nuovo modo)

In questo caso tutte le scritture vengono aggiunte a un file temporaneo ( log write-ahead ) e questo file viene periodicamente unito al database originale. Quando SQLite cerca qualcosa, per prima cosa controlla questo file temporaneo e, se non viene trovato nulla, procede con il file di database principale.

Di conseguenza, i lettori non competono con gli scrittori e le prestazioni sono molto migliori rispetto alla Vecchia Via.

Avvertenze

SQlite dipende in gran parte dalla funzionalità di chiusura del filesystem sottostante, quindi dovrebbe essere usata con caucanvas, maggiori dettagli qui

È anche probabile che tu corra nel database è un errore bloccato , specialmente in modalità journaled, quindi la tua app deve essere progettata tenendo presente questo errore

Questo thread è vecchio, ma penso che sarebbe bello condividere i risultati dei miei test su sqlite: ho eseguito 2 istanze di python program (diversi processi stesso programma) istruzioni di esecuzione SELECT e UPDATE comandi sql all’interno della transazione con il blocco EXCLUSIVE e il timeout impostato su 10 secondi per ottenere un lucchetto, e il risultato è stato frustrante. Ogni istanza ha fatto in 10000 step loop:

  • connettersi a db con blocco esclusivo
  • selezionare su una riga per leggere il contatore
  • aggiorna la riga con un nuovo valore uguale al contatore incrementato di 1
  • chiudere la connessione a db

Anche se sqlite ha concesso il blocco esclusivo della transazione, il numero totale di cicli realmente eseguiti non era uguale a 20.000 ma inferiore (il numero totale di iterazioni rispetto al contatore singolo è stato conteggiato per entrambi i processi). Il programma Python quasi non ha lanciato alcuna eccezione (solo una volta durante la selezione per 20 esecuzioni). la revisione sqlite al momento del test era 3.6.20 e python v3.3 CentOS 6.5. Secondo la mia opinione, è meglio trovare un prodotto più affidabile per questo tipo di lavoro o limitare le scritture a sqlite a processi / thread univoci.

SQLite supporta un numero illimitato di lettori simultanei, ma consentirà solo uno scrittore in qualsiasi istante nel tempo. Per molte situazioni, questo non è un problema. Il writer si accoda. Ogni applicazione fa funzionare rapidamente il suo database e si muove, e nessun blocco dura per più di qualche decina di millisecondi. Ma ci sono alcune applicazioni che richiedono più concorrenza e quelle applicazioni potrebbero dover cercare una soluzione diversa.

È chiaro nel DOC.

Nell’elaborazione delle transazioni, SQLite implementa l’elaborazione delle transazioni indipendente tramite i blocchi esclusivi e condivisi a livello di database. Ed è per questo che più processi possono leggere i dati dallo stesso database allo stesso tempo, ma solo uno può scrivere nel database.

È necessario ottenere un blocco esclusivo prima che un processo o un thread desideri eseguire un’operazione di scrittura su un database. Una volta ottenuto il blocco esclusivo, non verranno più eseguite altre operazioni di lettura o scrittura.

Dettagli dell’attrezzo per scrivere due di esempio:

SQLite ha una tabella di blocco per aiutare diversi database di scrittura possono essere bloccati all’ultimo momento per garantire la massima concorrenza.

Lo stato iniziale è “SBLOCCATO” e, in questo stato, la connessione non ha ancora accesso al database. Quando un database è connesso a un database e anche una transazione è stata avviata con BEGIN, la connessione è ancora nello stato “UNLOCKED”.

Lo stato successivo dello stato sbloccato è uno stato SHARED. Per poter leggere (non scrivere) i dati dal database, la connessione deve prima immettere lo stato SHARED, vale a dire, prima di ottenere un blocco SHARED. Connessioni multiple possono ottenere e mantenere blocchi SHARED allo stesso tempo, cioè più connessioni possono leggere i dati dallo stesso database allo stesso tempo. Ma anche se non è stato rilasciato un solo SHARED lock, non consente alcuna connessione per scrivere un database.

Se una connessione vuole scrivere un database, deve prima ottenere un blocco RISERVATO.

Solo un singolo blocco RESERVED può essere attivo in una volta, sebbene più blocchi SHARED possano coesistere con un solo blocco RISERVATO. RISERVATO differisce da PENDING in quel nuovo blocco SHARED può essere acquisito mentre c’è un blocco RISERVATO.

Una volta che una connessione ottiene un blocco RESERVED, può avviare l’elaborazione delle operazioni di modifica del database, sebbene queste modifiche possano essere eseguite solo nel buffer, anziché essere effettivamente scritte sul disco. Le modifiche apportate al contenuto di lettura vengono salvate nel buffer di memoria. Quando una connessione desidera inviare una modifica (o una transazione), è necessario aggiornare il blocco riservato a un blocco esclusivo. Per ottenere il blocco, devi prima sollevare il lucchetto su un lucchetto in sospeso.

Un blocco PENDING significa che il processo che blocca il blocco desidera scrivere sul database il prima ansible e attende solo che tutti i blocchi SHARED correnti vengano cancellati in modo che possa ottenere un blocco ESCLUSIVO. Non sono consentiti nuovi blocchi SHARED contro il database se è attivo un blocco PENDING, sebbene i blocchi SHARED esistenti possano continuare.

È necessario un blocco ESCLUSIVO per scrivere sul file di database. È consentito un solo blocco ESCLUSIVO sul file e nessun altro blocco di alcun tipo è consentito coesistere con un blocco ESCLUSIVO. Al fine di massimizzare la concorrenza, SQLite lavora per ridurre al minimo la quantità di tempo in cui i blocchi ESCLUSIVI sono trattenuti.

Quindi SQLite gestisce in modo sicuro l’accesso simultaneo da più processi scrivendo dallo stesso db perché non lo supporta. Otterrai SQLITE_BUSY o SQLITE_LOCKED per il secondo writer quando raggiungerà la limitazione dei tentativi.