Tôi đã viết một ứng dụng java mà thỉnh thoảng ghi các sự kiện vào một cơ sở dữ liệu SQLite từ nhiều luồng. Tôi đã nhận thấy rằng tôi có thể kích hoạt các lỗi "Database Locked" của SQLite tương đối dễ dàng bằng cách sinh ra một số lượng nhỏ các sự kiện cùng một lúc. Điều này đã thúc đẩy tôi viết một chương trình thử nghiệm bắt chước hành vi của trường hợp xấu nhất và tôi đã rất ngạc nhiên khi thấy SQLite hoạt động kém như thế nào trong trường hợp sử dụng này. Mã được đăng dưới đây chỉ cần thêm năm bản ghi vào cơ sở dữ liệu, đầu tiên tuần tự để có được các giá trị "kiểm soát". Sau đó, năm bản ghi cùng được thêm đồng thời.SQLite trong một ứng dụng java đa luồng
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);
}
}
}
}
Đây là kết quả khi tôi chạy mã java này:
[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
Chèn 5 hồ sơ liên tục mất khoảng 750 mili giây, tôi mong chờ sự chèn đồng thời lấy khoảng cùng một lượng thời gian. Nhưng bạn có thể thấy rằng cho một thời gian chờ 3 giây họ thậm chí không kết thúc. Tôi cũng đã viết một chương trình thử nghiệm tương tự trong C, sử dụng các cuộc gọi thư viện bản địa của SQLite và chèn đồng thời hoàn thành trong khoảng thời gian tương tự như các chèn đồng thời đã làm. Vì vậy, vấn đề là với thư viện java của tôi.
Đây là kết quả khi tôi chạy phiên bả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
Tôi đã thử mã này với hai tài xế khác nhau JDBC (http://www.zentus.com/sqlitejdbc và http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC), và các wrapper sqlite4java. Mỗi lần kết quả tương tự nhau. Có ai ngoài đó biết về một thư viện SQLite cho java mà không có hành vi này?
Vì vậy, nếu gốc của vấn đề là khóa hệ thống tập tin, tại sao mã C của tôi chạy nhanh như vậy? – jlunavtgrad
nhanh chóng! Sử dụng cùng một kết nối JDBC hoàn toàn giải quyết được vấn đề. Bây giờ tôi thấy hiệu suất tốt hơn hoặc bằng mã C của tôi. Tôi đã thử điều này với chương trình java ở trên ngày hôm qua, nhưng bây giờ tôi nhận ra rằng tôi đã ghi đè kết nối của mình bằng một cái mới cho mỗi lần chèn. – jlunavtgrad
Xem thêm: http://stackoverflow.com/questions/24513576/opening-database-connection-with-the-sqlite-open-nomutex-flag-in-java – Stephan