¿SQLite3 maneja de manera segura el acceso concurrente mediante múltiples procesos de lectura / escritura desde el mismo DB? ¿Hay alguna excepción de la plataforma a eso?
¿SQLite3 maneja de manera segura el acceso concurrente mediante múltiples procesos de lectura / escritura desde el mismo DB? ¿Hay alguna excepción de la plataforma a eso?
Respuestas:
Si la mayoría de esos accesos concurrentes son lecturas (por ejemplo, SELECT), SQLite puede manejarlos muy bien. Pero si comienza a escribir simultáneamente, la contención de bloqueo podría convertirse en un problema. Mucho dependerá de lo rápido que sea su sistema de archivos, ya que el motor SQLite es extremadamente rápido y tiene muchas optimizaciones inteligentes para minimizar la contención. Especialmente SQLite 3.
Para la mayoría de las aplicaciones de escritorio / laptop / tablet / teléfono, SQLite es lo suficientemente rápido ya que no hay suficiente concurrencia. (Firefox usa SQLite ampliamente para marcadores, historial, etc.)
En el caso de las aplicaciones de servidor, alguien dijo hace algún tiempo que una base de datos SQLite podía manejar perfectamente menos de 100K vistas de página por día en escenarios típicos (por ejemplo, blogs, foros), y aún no he visto ninguna evidencia de lo contrario. De hecho, con los discos y procesadores modernos, el 95% de los sitios web y servicios web funcionarían perfectamente con SQLite.
Si desea un acceso de lectura / escritura realmente rápido, use una base de datos SQLite en memoria . La RAM es varios órdenes de magnitud más rápida que el disco.
Sí, SQLite maneja bien la concurrencia, pero no es lo mejor desde un ángulo de rendimiento. Por lo que puedo decir, no hay excepciones a eso. Los detalles están en el sitio de SQLite: https://www.sqlite.org/lockingv3.html
Esta declaración es de interés: "El módulo de buscapersonas se asegura de que los cambios sucedan de una vez, que todos los cambios ocurran o ninguno de ellos, que dos o más procesos no intenten acceder a la base de datos de manera incompatible al mismo tiempo"
Si lo hace Vamos a descubrir por qué
Todos los cambios dentro de una sola transacción en SQLite ocurren completamente o no ocurren
Dicha compatibilidad con ACID, así como las lecturas / escrituras simultáneas, se proporcionan de 2 maneras: usando el llamado registro en diario (llamémoslo " forma antigua ") o registro de escritura anticipada (lo llamamos " forma nueva ")
En este modo, SQLite utiliza el bloqueo DATABASE-LEVEL . Este es el punto crucial para entender.
Eso significa que cada vez que necesita leer / escribir algo, primero adquiere un bloqueo en el archivo de base de datos COMPLETO . Múltiples lectores pueden coexistir y leer algo en paralelo
Durante la escritura se asegura de adquirir un bloqueo exclusivo y de que ningún otro proceso es lectura / escritura simultánea y, por lo tanto, las escrituras son seguras.
Es por eso que aquí dicen que SQlite implementa transacciones serializables
Ya que necesita bloquear una base de datos completa cada vez y todos esperan un proceso de manejo de escritura, la concurrencia sufre y tales escrituras / lecturas concurrentes tienen un rendimiento bastante bajo
Antes de escribir algo en el archivo de base de datos, SQLite primero guardaría el fragmento para cambiarlo en un archivo temporal. Si algo falla en el medio de la escritura en el archivo de la base de datos, recogerá este archivo temporal y revertirá los cambios.
En este caso, todas las escrituras se agregan a un archivo temporal ( registro de escritura anticipada ) y este archivo se fusiona periódicamente con la base de datos original. Cuando SQLite busca algo, primero verificará este archivo temporal y, si no encuentra nada, continúe con el archivo de la base de datos principal.
Como resultado, los lectores no compiten con los escritores y el rendimiento es mucho mejor en comparación con el Old Way.
SQlite depende en gran medida de la funcionalidad de bloqueo del sistema de archivos subyacente, por lo que debe usarse con precaución, más detalles aquí
También es probable que se encuentre con un error de bloqueo de la base de datos , especialmente en el modo registrado, por lo que su aplicación debe diseñarse teniendo en cuenta este error
Nadie parece haber mencionado el modo WAL (Write Ahead Log). Asegúrese de que las transacciones estén organizadas correctamente y con el modo WAL activado, no es necesario mantener la base de datos bloqueada mientras las personas leen mientras se realiza una actualización.
El único problema es que en algún momento el WAL necesita ser reincorporado a la base de datos principal, y lo hace cuando se cierra la última conexión a la base de datos. Con un sitio muy ocupado, es posible que le tome unos segundos cerrar todas las conexiones, pero 100K visitas por día no deberían ser un problema.
database is locked
el error será subida por el escritor
En 2019, hay dos nuevas opciones de escritura simultánea que aún no se han lanzado, pero que están disponibles en sucursales separadas.
La ventaja de este modo de diario sobre el modo "wal" normal es que los escritores pueden continuar escribiendo en un archivo wal mientras el otro está marcado.
COMIENCE CONCURRENTE - enlace al documento detallado
La mejora BEGIN CONCURRENT permite que varios escritores procesen transacciones de escritura simultáneamente si la base de datos está en modo "wal" o "wal2", aunque el sistema aún serializa comandos COMMIT.
Cuando se abre una transacción de escritura con "COMENZAR CONCURRENTE", el bloqueo real de la base de datos se aplaza hasta que se ejecuta un COMPROMISO. Esto significa que cualquier cantidad de transacciones iniciadas con BEGIN CONCURRENT puede proceder simultáneamente. El sistema utiliza un bloqueo de nivel de página optimista para evitar la confirmación de transacciones concurrentes en conflicto.
Juntos están presentes en begin-concurrent-wal2 o cada uno en una rama propia separada .
begin concurrent
cosas.
SQLite tiene un bloqueo de lectores y escritores en el nivel de la base de datos. Múltiples conexiones (posiblemente propiedad de diferentes procesos) pueden leer datos de la misma base de datos al mismo tiempo, pero solo uno puede escribir en la base de datos.
SQLite admite un número ilimitado de lectores simultáneos, pero solo permitirá un escritor en cualquier momento. Para muchas situaciones, esto no es un problema. El escritor hace cola. Cada aplicación hace que su base de datos funcione rápidamente y avanza, y ningún bloqueo dura más de unas pocas docenas de milisegundos. Pero hay algunas aplicaciones que requieren más concurrencia, y esas aplicaciones pueden necesitar buscar una solución diferente. - Usos apropiados para SQLite @ SQLite.org
El bloqueo de lectores y escritores permite el procesamiento de transacciones independientes y se implementa utilizando bloqueos exclusivos y compartidos en el nivel de la base de datos.
Se debe obtener un bloqueo exclusivo antes de que una conexión realice una operación de escritura en una base de datos. Después de obtener el bloqueo exclusivo, las operaciones de lectura y escritura de otras conexiones se bloquean hasta que se libera nuevamente el bloqueo.
SQLite tiene una tabla de bloqueo que ayuda a bloquear la base de datos lo más tarde posible durante una operación de escritura para garantizar la máxima concurrencia.
El estado inicial es DESBLOQUEADO, y en este estado, la conexión aún no ha accedido a la base de datos. Cuando un proceso se conecta a una base de datos e incluso se inicia una transacción con BEGIN, la conexión aún está en el estado DESBLOQUEADO.
Después del estado DESBLOQUEADO, el siguiente estado es el estado COMPARTIDO. Para poder leer (no escribir) datos de la base de datos, la conexión primero debe ingresar al estado COMPARTIDO, obteniendo un bloqueo COMPARTIDO. Múltiples conexiones pueden obtener y mantener bloqueos COMPARTIDOS al mismo tiempo, por lo que múltiples conexiones pueden leer datos de la misma base de datos al mismo tiempo. Pero mientras solo un bloqueo COMPARTIDO permanezca inédito, ninguna conexión puede completar con éxito una escritura en la base de datos.
Si una conexión quiere escribir en la base de datos, primero debe obtener un bloqueo RESERVADO.
Solo puede estar activo un solo bloqueo RESERVADO a la vez, aunque varios bloqueos COMPARTIDOS pueden coexistir con un solo bloqueo RESERVADO. RESERVADO difiere de PENDIENTE en que se pueden adquirir nuevas cerraduras COMPARTIDAS mientras haya una cerradura RESERVADA. - Bloqueo y concurrencia de archivos en SQLite versión 3 @ SQLite.org
Una vez que una conexión obtiene un bloqueo RESERVADO, puede comenzar a procesar las operaciones de modificación de la base de datos, aunque estas modificaciones solo pueden realizarse en el búfer, en lugar de escribirse realmente en el disco. Las modificaciones realizadas en el contenido de lectura se guardan en el búfer de memoria. Cuando una conexión desea enviar una modificación (o transacción), es necesario actualizar el bloqueo RESERVADO a un bloqueo EXCLUSIVO. Para obtener el bloqueo, primero debe levantar el bloqueo a un bloqueo PENDIENTE.
Un bloqueo PENDIENTE significa que el proceso que contiene el bloqueo desea escribir en la base de datos lo antes posible y solo está esperando que todos los bloqueos COMPARTIDOS actuales se borren para que pueda obtener un bloqueo EXCLUSIVO. No se permiten nuevos bloqueos COMPARTIDOS en la base de datos si un bloqueo PENDIENTE está activo, aunque los bloqueos COMPARTIDOS existentes pueden continuar.
Se necesita un bloqueo EXCLUSIVO para escribir en el archivo de base de datos. Solo se permite un bloqueo EXCLUSIVO en el archivo y ningún otro bloqueo de ningún tipo puede coexistir con un bloqueo EXCLUSIVO. Para maximizar la concurrencia, SQLite trabaja para minimizar la cantidad de tiempo que se mantienen los bloqueos EXCLUSIVOS. - Bloqueo y concurrencia de archivos en SQLite versión 3 @ SQLite.org
¡Entonces podría decir que SQLite maneja de manera segura el acceso concurrente mediante múltiples procesos que escriben en el mismo DB simplemente porque no lo admite! Obtendrá SQLITE_BUSY
o SQLITE_LOCKED
para el segundo escritor cuando llegue a la limitación de reintento.
Este hilo es antiguo, pero creo que sería bueno compartir el resultado de mis pruebas realizadas en sqlite: ejecuté 2 instancias del programa python (diferentes procesos, el mismo programa) ejecutando instrucciones SELECT y UPDATE comandos sql dentro de la transacción con EXCLUSIVE lock and timeout establecido en 10 segundos para obtener un bloqueo, y el resultado fue frustrante. Cada instancia lo hizo en un bucle de 10000 pasos:
Incluso si a sqlite se le otorgara un bloqueo exclusivo en la transacción, el número total de ciclos realmente ejecutados no fue igual a 20 000, sino menos (número total de iteraciones en un solo contador contado para ambos procesos). El programa Python casi no arrojó ninguna excepción (solo una vez durante la selección para 20 ejecuciones). La revisión de sqlite en el momento de la prueba fue 3.6.20 y Python v3.3 CentOS 6.5. En mi opinión, es mejor encontrar un producto más confiable para este tipo de trabajo o restringir las escrituras a sqlite a un único proceso / hilo único.
with con
es suficiente.