Im WAL-Modus gesperrte Datenbank nur mit Lesern

Verwenden von System.Data.Sqlite 1.0.86.0 (einschließlich SQLite 3.7.17) inVorausschreibende Protokollierung Im aktuellen Modus treten beim gleichzeitigen Lesen Datenbanksperren auf. Dies sollte nicht der Fall sein, wenn ich WAL richtig verstehe. Ich schreibe oder begebe nichts und dasReadCommitted Der Transaktionsisolationsmodus wird korrekt verwendet, um das Serialisieren von Lesevorgängen zu vermeiden.

SQLite DB (mit WAL) ist bei der Vorbereitung einer "select" -Anweisung gesperrt - warum? ist ein ähnliches Problem. Die einzige Antwort spricht vom Anrufensqlite3_reset nach jedemsqlite3_step, was von System.Data.Sqlite korrekt gemacht wird, soweit ich das im Quellcode gesehen habe.

Vollständige Reproduktion:

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);
    }

}

Ausgabe:

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;

Sie können sehen, dass die letzte um eine Größenordnung langsamer ist. Wenn im Debug-Modus ausgeführt, wird Folgendes im Ausgabefenster je nach Ausführung ein oder mehrere Male protokolliert:

SQLite-Fehler (261): Datenbank ist gesperrt

Haben Sie eine Idee, wie Sie diese Blockierung vermeiden können? Natürlich kann in diesem Beispiel WAL einfach deaktiviert werden, aber in einem realen Projekt kann ich nicht: Ich benötige potenzielle Schreibvorgänge, um sofort erfolgreich zu sein, selbst wenn eine lange Lesetransaktion ausgeführt wird.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage