J'ai écrit une application java qui enregistre de manière sporadique des événements dans une base de données SQLite à partir de plusieurs threads. J'ai remarqué que je peux déclencher assez facilement les erreurs SQLite de "Database Locked" en générant un petit nombre d'événements en même temps. Cela m'a conduit à écrire un programme de test qui imite le pire des cas et j'ai été surpris par la façon dont il semble que SQLite se comporte mal dans ce cas d'utilisation. Le code affiché ci-dessous ajoute simplement cinq enregistrements à une base de données, d'abord séquentiellement pour obtenir des valeurs de «contrôle». Ensuite, les mêmes cinq enregistrements sont ajoutés simultanément.SQLite dans une application java multithread
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);
}
}
}
}
Voici la sortie quand je lance ce code 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
Insertion 5 enregistrements séquentiellement prend environ 750 millisecondes, j'attendre les insertions concurrentes à prendre à peu près la même quantité de temps. Mais vous pouvez voir que, compte tenu d'un délai de 3 secondes, ils ne finissent même pas. J'ai également écrit un programme de test similaire en C, en utilisant les appels de bibliothèque natifs de SQLite et les insertions simultanées terminées à peu près en même temps que les insertions simultanées. Donc, le problème est avec ma bibliothèque Java.
est ici la sortie quand je lance la version 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
J'ai essayé ce code avec deux pilotes JDBC différents (http://www.zentus.com/sqlitejdbc et http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC), et l'emballage de sqlite4java. Chaque fois que les résultats étaient similaires. Est-ce que quelqu'un sait là-bas une bibliothèque SQLite pour Java qui n'a pas ce comportement?
Donc, si la racine du problème est les verrous du système de fichiers, pourquoi mon code C tourne-t-il aussi vite? – jlunavtgrad
hourra! L'utilisation de la même connexion JDBC a totalement résolu le problème. Je vois maintenant une performance supérieure ou égale à mon code C. J'avais essayé cela avec le programme Java ci-dessus hier, mais je réalise maintenant que je remplaçais ma connexion avec un nouveau pour chaque insertion. – jlunavtgrad
Voir aussi: http://stackoverflow.com/questions/24513576/opening-database-connection-with-the-sqlite-open-nomutex-flag-in-java – Stephan