the sqlite_busy java sqlite jdbc sqlite3 locked

java - sqlite_busy the database file is locked database is locked



SQLite en una aplicación java multiproceso (2)

Este es un problema con la biblioteca SQLite central , no con ningún contenedor de Java. SQLite utiliza bloqueos basados ​​en sistemas de archivos para la sincronización de acceso concurrente entre procesos, ya que como una base de datos integrada no tiene un proceso dedicado (servidor) para programar las operaciones. Dado que cada hilo en su código crea su propia conexión a la base de datos, se trata como un proceso separado, con la sincronización que se realiza mediante bloqueos basados ​​en archivos, que son significativamente más lentos que cualquier otro método de sincronización.

Además, SQLite no admite el bloqueo por fila (¿todavía?). Esencialmente, todo el archivo de base de datos se locked para cada operación. Si tiene suerte y su sistema de archivos admite bloqueos de rango de bytes, es posible que múltiples lectores accedan a su base de datos simultáneamente, pero no debe asumir ese tipo de comportamiento.

La biblioteca central de SQLite de forma predeterminada permite que varios subprocesos utilicen la misma conexión simultáneamente sin ningún problema. Supongo que cualquier envoltorio sano de JDBC también permitirá ese comportamiento en los programas Java, aunque en realidad no lo he intentado.

Por lo tanto tienes dos soluciones:

  • Comparte la misma conexión JDBC entre todos los hilos.

  • Ya que los desarrolladores de SQLite parecen pensar que los subprocesos son malos , sería mejor que un subproceso maneje todas las operaciones de su base de datos y serialice las tareas de la base de datos por su cuenta utilizando el código Java ...

Es posible que desee echar un vistazo a esta vieja pregunta mía : parece haber acumulado varios consejos para mejorar el rendimiento de las actualizaciones en SQLite a lo largo del tiempo.

He escrito una aplicación java que registra eventos esporádicamente en una base de datos SQLite desde varios subprocesos. Me he dado cuenta de que puedo desencadenar errores de "Base de datos bloqueados" de SQLite con relativa facilidad al generar una pequeña cantidad de eventos al mismo tiempo. Esto me llevó a escribir un programa de prueba que imita el comportamiento del peor de los casos y me sorprendió lo mal que parece que el SQLite se desempeña en este caso de uso. El código publicado a continuación simplemente agrega cinco registros a una base de datos, primero de forma secuencial para obtener los valores de "control". Luego los mismos cinco registros se agregan simultáneamente.

import java.sql.*; public class Main { public static void main(String[] args) throws Exception { Class.forName("org.sqlite.JDBC"); Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db"); Statement stat = conn.createStatement(); stat.executeUpdate("drop table if exists people"); stat.executeUpdate("create table people (name, occupation)"); conn.close(); SqlTask tasks[] = { new SqlTask("Gandhi", "politics"), new SqlTask("Turing", "computers"), new SqlTask("Picaso", "artist"), new SqlTask("shakespeare", "writer"), new SqlTask("tesla", "inventor"), }; System.out.println("Sequential DB access:"); Thread threads[] = new Thread[tasks.length]; for(int i = 0; i < tasks.length; i++) threads[i] = new Thread(tasks[i]); for(int i = 0; i < tasks.length; i++) { threads[i].start(); threads[i].join(); } System.out.println("Concurrent DB access:"); for(int i = 0; i < tasks.length; i++) threads[i] = new Thread(tasks[i]); for(int i = 0; i < tasks.length; i++) threads[i].start(); for(int i = 0; i < tasks.length; i++) threads[i].join(); } private static class SqlTask implements Runnable { String name, occupation; public SqlTask(String name, String occupation) { this.name = name; this.occupation = occupation; } public void run() { Connection conn = null; PreparedStatement prep = null; long startTime = System.currentTimeMillis(); try { try { conn = DriverManager.getConnection("jdbc:sqlite:test.db"); prep = conn.prepareStatement("insert into people values (?, ?)"); prep.setString(1, name); prep.setString(2, occupation); prep.executeUpdate(); long duration = System.currentTimeMillis() - startTime; System.out.println(" SQL Insert completed: " + duration); } finally { if (prep != null) prep.close(); if (conn != null) conn.close(); } } catch(SQLException e) { long duration = System.currentTimeMillis() - startTime; System.out.print(" SQL Insert failed: " + duration); System.out.println(" SQLException: " + e); } } } }

Aquí está la salida cuando ejecuto este código java:

[java] Sequential DB access: [java] SQL Insert completed: 132 [java] SQL Insert completed: 133 [java] SQL Insert completed: 151 [java] SQL Insert completed: 134 [java] SQL Insert completed: 125 [java] Concurrent DB access: [java] SQL Insert completed: 116 [java] SQL Insert completed: 1117 [java] SQL Insert completed: 2119 [java] SQL Insert failed: 3001 SQLException: java.sql.SQLException: database locked [java] SQL Insert completed: 3136

Insertar 5 registros de forma secuencial lleva aproximadamente 750 milisegundos, esperaría que las inserciones simultáneas tomen aproximadamente la misma cantidad de tiempo. Pero puedes ver que dado un tiempo de espera de 3 segundos, ni siquiera terminan. También escribí un programa de prueba similar en C, utilizando las llamadas de la biblioteca nativa de SQLite y las inserciones simultáneas terminaron aproximadamente al mismo tiempo que las inserciones simultáneas. Así que el problema es con mi biblioteca java.

Aquí está la salida cuando ejecuto la versión C:

Sequential DB access: SQL Insert completed: 126 milliseconds SQL Insert completed: 126 milliseconds SQL Insert completed: 126 milliseconds SQL Insert completed: 125 milliseconds SQL Insert completed: 126 milliseconds Concurrent DB access: SQL Insert completed: 117 milliseconds SQL Insert completed: 294 milliseconds SQL Insert completed: 461 milliseconds SQL Insert completed: 662 milliseconds SQL Insert completed: 862 milliseconds

Probé este código con dos controladores JDBC diferentes ( http://www.zentus.com/sqlitejdbc y http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC ), y el contenedor sqlite4java. Cada vez los resultados fueron similares. ¿Alguien por ahí sabe de una biblioteca SQLite para Java que no tiene este comportamiento?


Yo uso la misma conexión para varios hilos. Además, tuve que sincronizar los métodos db-write.