System.Data.Sqlite 1.0.86.0 (SQLite 3.7.17 を含む) をWrite-Ahead Loggingモードで使用すると、同時読み取り中にデータベースのロックが発生しますが、WAL を正しく理解していれば発生しないはずです。私は何も書いたりコミットしたりしておらず、ReadCommitted
読み取りのシリアル化を避けるためにトランザクション分離モードが正しく使用されています。
「選択」ステートメントを準備するときに SQLite DB (WAL を使用) がロックされる - なぜですか? 同様の問題です。唯一の答えは、ソースコードで見た限り、 System.Data.Sqlite によって正しく行われるsqlite3_reset
each の後に呼び出すことについてです。sqlite3_step
完全再現:
internal static class Program {
private const string DbFileName = "test.sqlite";
private static readonly string _connectionString = BuildConnectionString(DbFileName);
internal static void Main() {
File.Delete(DbFileName);
ExecuteSql("CREATE TABLE Test (Id INT NOT NULL, Name TEXT);", true);
for (int i = 0; i < 10; i++)
Task.Run(() => ExecuteSql("SELECT Id, Name FROM Test;", false));
Console.ReadKey();
}
private static string BuildConnectionString(string fileName) {
var builder = new SQLiteConnectionStringBuilder {
DataSource = fileName,
DateTimeFormat = SQLiteDateFormats.ISO8601,
DefaultIsolationLevel = IsolationLevel.ReadCommitted,
ForeignKeys = true,
JournalMode = SQLiteJournalModeEnum.Wal,
SyncMode = SynchronizationModes.Full
};
return builder.ToString();
}
private static void ExecuteSql(string sql, bool commit) {
Stopwatch stopwatch = Stopwatch.StartNew();
using (var connection = new SQLiteConnection(_connectionString)) {
connection.Open();
using (SQLiteTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted)) {
using (SQLiteCommand command = connection.CreateCommand()) {
command.CommandText = sql;
command.ExecuteNonQuery();
}
if (commit)
transaction.Commit();
}
}
stopwatch.Stop();
Console.WriteLine("{0}: {1}", stopwatch.Elapsed, sql);
}
}
出力:
00:00:00.1927492: CREATE TABLE Test (Id INT NOT NULL, Name TEXT);
00:00:00.0054247: SELECT Id, Name FROM Test;
00:00:00.0055334: SELECT Id, Name FROM Test;
00:00:00.0056022: SELECT Id, Name FROM Test;
00:00:00.0054860: SELECT Id, Name FROM Test;
00:00:00.0053894: SELECT Id, Name FROM Test;
00:00:00.0056843: SELECT Id, Name FROM Test;
00:00:00.0006604: SELECT Id, Name FROM Test;
00:00:00.0006758: SELECT Id, Name FROM Test;
00:00:00.0097950: SELECT Id, Name FROM Test;
00:00:00.0980008: SELECT Id, Name FROM Test;
最後の 1 つは桁違いに遅いことがわかります。デバッグ モードで実行すると、実行に応じて、次の内容が出力ウィンドウに 1 回以上記録されます。
SQLite エラー (261): データベースがロックされています
このロックを回避する方法はありますか? もちろん、このサンプルでは WAL を簡単にオフにすることができますが、実際のプロジェクトではできません。長時間の読み取りトランザクションが行われている場合でも、すぐに成功する可能性のある書き込みが必要です。