¿Cómo usar SQLite en una aplicación multiproceso?

Estoy desarrollando una aplicación con SQLite como base de datos, y estoy teniendo problemas para entender cómo usarlo en varios hilos (desafortunadamente, ninguna de las otras preguntas sobre el Desbordamiento de stack realmente me ayudó).

Mi caso de uso: la base de datos tiene una tabla, llamémosla “A”, que tiene diferentes grupos de filas (basadas en una de sus columnas). Tengo el “hilo principal” de la aplicación que lee el contenido de la tabla A. Además, decido, de vez en cuando, actualizar un cierto grupo de filas. Para hacer esto, quiero engendrar un nuevo hilo, eliminar todas las filas del grupo y volver a insertarlas (esa es la única manera de hacerlo en el contexto de mi aplicación). Esto podría sucederle a diferentes grupos al mismo tiempo, por lo que podría tener más de 2 hilos tratando de actualizar la base de datos.

Estoy usando diferentes transacciones de cada hilo, IE al comienzo del ciclo de actualización de cada hilo, tengo un comienzo. De hecho, lo que cada hilo realmente hace es llamar “BEGIN”, eliminar de la base de datos todas las filas que necesita para “actualizar”, y las inserta de nuevo con los nuevos valores (esta es la forma en que debe hacerse en el contexto de mi solicitud).

Ahora, estoy tratando de entender cómo voy a implementar esto. Intenté leer todo (otras respuestas en Stack Overflow, el sitio de SQLite) pero no encontré todas las respuestas. Aquí hay algunas cosas que me pregunto:

  1. ¿Debo llamar “abrir” y crear una nueva estructura sqlite de cada hilo?
  2. ¿Debo agregar algún código especial para todo esto, o es suficiente para engendrar diferentes hilos, actualizar las filas, y eso está bien (ya que estoy usando diferentes transacciones)?
  3. Vi algo que hablaba sobre los diferentes tipos de cerraduras que existen, y el hecho de que podría recibir “SQLite busy” de llamar a ciertas API, pero sinceramente no vi ninguna referencia que se explicara por completo cuando necesito tener todo esto en cuenta. ¿Realmente necesito?

Si alguien puede responder las preguntas / señalarme en la dirección de un buen recurso, estaría muy agradecido.

ACTUALIZACIÓN 1: por todo lo que he leído hasta ahora, parece que no puede tener dos hilos que van a escribir en un archivo de base de datos de todos modos.

Ver: http://www.sqlite.org/lockingv3.html . En la sección 3.0: Un locking RESERVADO significa que el proceso está planeando escribir en el archivo de base de datos en algún momento en el futuro, pero que actualmente solo está leyendo desde el archivo. Solo un locking RESERVADO puede estar activo a la vez, aunque varios lockings COMPARTIDOS pueden coexistir con un único locking RESERVADO.

¿Esto significa que también podría generar solo un hilo para actualizar un grupo de filas cada vez? Es decir, ¿tengo algún tipo de hilo de sondeo que decide que necesito actualizar algunas de las filas, y luego crea un nuevo hilo para hacerlo, pero nunca más de una en una? Dado que parece que cualquier otro hilo que creamos, obtendrá SQLITE_BUSY hasta que termine el primer hilo, de todos modos.

¿He entendido bien las cosas?

Por cierto, gracias por las respuestas hasta ahora, han ayudado mucho.

Mira este enlace . La forma más fácil es realizar el locking usted mismo y evitar compartir la conexión entre hilos. Otro buen recurso se puede encontrar aquí , y concluye con:

  1. Asegúrese de comstackr SQLite con -DTHREADSAFE = 1.

  2. Asegúrese de que cada subproceso abra el archivo de base de datos y mantenga su propia estructura sqlite.

  3. Asegúrese de manejar la posible posibilidad de que uno o más hilos colisionen cuando acceden al archivo db al mismo tiempo: maneje SQLITE_BUSY de manera apropiada.

  4. Asegúrese de incluir en las transacciones los comandos que modifican el archivo de la base de datos, como INSERT, UPDATE, DELETE y otros.

Algunos pasos al comenzar con SQLlite para uso multiproceso:

  1. Asegúrese de que sqlite esté comstackdo con la bandera multihilo.
  2. Debe llamar a abrir su archivo sqlite para crear una conexión en cada subproceso, no comparta conexiones entre subprocesos.
  3. SQLite tiene un modelo de subprocesamiento muy conservador, cuando realiza una operación de escritura, que incluye abrir transacciones que están por hacer un INSERT / UPDATE / DELETE, otros subprocesos serán bloqueados hasta que se complete esta operación.
  4. Si no usa una transacción, entonces las transacciones son implícitas, por lo que si inicia un INSERT / DELETE / UPDATE, sqlite intentará adquirir un locking exclusivo y completará la operación antes de liberarlo.
  5. Si realiza una instrucción BEGIN EXCLUSIVE, obtendrá un locking exclusivo antes de realizar operaciones en esa transacción. Un COMMIT o ROLLBACK liberará el locking.
  6. Su sqlite3_step, sqlite3_prepare y algunas otras llamadas pueden devolver SQLITE_BUSY o SQLITE_LOCKED. SQLITE_BUSY generalmente significa que sqlite necesita adquirir el locking. La mayor diferencia entre los dos valores de retorno:
    • SQLITE_LOCKED: si obtiene esto de una sentencia sqlite3_step, DEBE llamar a sqlite3_reset en el descriptor de contexto de la sentencia. Debería obtener esto solo en la primera llamada a sqlite3_step, de modo que una vez que se haya restablecido, puede “volver a intentar” su llamada sqlite3_step. En otras operaciones, es lo mismo que SQLITE_BUSY
    • SQLITE_BUSY: no hay necesidad de llamar a sqlite3_reset, solo vuelve a intentar tu operación después de esperar un poco para que se libere el locking.

Me doy cuenta de que este es un hilo viejo y las respuestas son buenas, pero he estado investigando esto recientemente y encontré un análisis interesante de algunas implementaciones diferentes. Principalmente repasa las fortalezas y debilidades del intercambio de conexiones, el envío de mensajes, las conexiones locales de subprocesos y la agrupación de conexiones. Échale un vistazo aquí: http://dev.yorhel.nl/doc/sqlaccess

Verifique este código de la wiki de SQLite .

He hecho algo similar con C y he cargado el código aquí .

Espero que sea útil.

Las versiones modernas de SQLite tienen habilitada la seguridad de subprocesos por defecto. SQLITE_THREADSAFE comstackción SQLITE_THREADSAFE controla si el código se incluye o no en SQLite para permitir que funcione de forma segura en un entorno multiproceso. El valor predeterminado es SQLITE_THREADSAFE=1 . Significa el modo serializado . En este modo:

En este modo (que es el predeterminado cuando SQLite se comstack con SQLITE_THREADSAFE = 1) la biblioteca SQLite serializará el acceso a las conexiones de base de datos y las declaraciones preparadas para que la aplicación pueda usar libremente la misma conexión de base de datos o la misma statement preparada en diferentes hilos al mismo tiempo.

Utilice la sqlite3_threadsafe() para verificar la SQLITE_THREADSAFE comstackción SQLITE_THREADSAFE de la biblioteca SQLITE_THREADSAFE .

El comportamiento de seguridad del hilo de la biblioteca predeterminado se puede cambiar a través de sqlite3_config() . Utilice los SQLITE_OPEN_NOMUTEX y SQLITE_OPEN_FULLMUTEX en sqlite3_open_v2() para ajustar el modo de subprocesamiento de las conexiones de bases de datos individuales.

Resumen

Las transacciones en SQLite son SERIALIZABLE.

Los cambios realizados en una conexión de base de datos son invisibles para todas las otras conexiones de bases de datos antes de la confirmación.

Una consulta ve todos los cambios que se completan en la misma conexión de base de datos antes del inicio de la consulta, independientemente de si esos cambios se han confirmado o no.

Si se producen cambios en la misma conexión de la base de datos después de que se inicia una consulta, pero antes de que se complete la consulta, no está definido si la consulta verá esos cambios o no.

Si se producen cambios en la misma conexión de base de datos después de que una consulta se inicia pero antes de que la consulta finalice, la consulta puede devolver una fila modificada más de una vez, o puede devolver una fila que se eliminó previamente.

A los efectos de los cuatro elementos anteriores, se considera que dos conexiones de bases de datos que usan el mismo caché compartido y que permiten que PRAGMA read_uncommitted sea ​​la misma conexión de base de datos, no conexiones de base de datos separadas.


Además de la información anterior sobre el acceso multiproceso, podría valer la pena echar un vistazo a esta página en aislamiento , ya que muchas cosas han cambiado desde esta pregunta original y la introducción del registro de escritura anticipada ( WAL ).

Parece que un enfoque híbrido de tener varias conexiones abiertas a la base de datos proporciona garantías de concurrencia adecuadas, intercambiando los gastos de apertura de una nueva conexión con el beneficio de permitir transacciones de escritura de subprocesos múltiples.