Ho scritto un'applicazione java che registra sporadicamente gli eventi in un database SQLite da più thread. Ho notato che posso innescare gli errori di "Database Locked" di SQLite relativamente facilmente, generando un piccolo numero di eventi contemporaneamente. Questo mi ha spinto a scrivere un programma di test che simula il comportamento del caso peggiore e sono rimasto sorpreso da quanto male sembra che SQLite funzioni in questo caso d'uso. Il codice pubblicato di seguito aggiunge semplicemente cinque record a un database, in primo luogo in modo sequenziale per ottenere valori di "controllo". Quindi gli stessi cinque record vengono aggiunti contemporaneamente.SQLite in un'applicazione java con multithreading
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);
}
}
}
}
Ecco l'output quando si esegue questo codice 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
Inserimento 5 record in modo sequenziale richiede circa 750 millisecondi, ci si aspetterebbe inserti simultanei a prendere all'incirca la stessa quantità di tempo. Ma puoi vedere che dato un timeout di 3 secondi non finiscono nemmeno. Ho anche scritto un programma di test simile in C, usando le chiamate alle librerie native di SQLite e gli inserimenti simultanei finiti all'incirca nello stesso momento in cui gli inserti concorrenti facevano. Quindi il problema è con la mia libreria java.
Ecco l'output quando eseguo la versione 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
ho provato questo codice con due diversi driver JDBC (http://www.zentus.com/sqlitejdbc e http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC), e l'involucro sqlite4java. Ogni volta i risultati erano simili. Qualcuno là fuori sa di una libreria SQLite per java che non ha questo comportamento?
Quindi, se la radice del problema è il blocco del filesystem, perché il mio codice C funziona alla stessa velocità di quello? – jlunavtgrad
evviva! L'utilizzo della stessa connessione JDBC ha risolto completamente il problema. Ora vedo prestazioni migliori o uguali al mio codice C. L'ho provato con il precedente programma java ieri, ma ora mi rendo conto che stavo sovrascrivendo la mia connessione con una nuova per ogni inserto. – jlunavtgrad
Vedi anche: http://stackoverflow.com/questions/24513576/opening-database-connection-with-the-sqlite-open-nomutex-flag-in-java – Stephan