11

我正在使用 System.Data.Sqlite 在 C# 中访问 SQLite 数据库。我有一个查询必须通读表中的行。在遍历行和阅读器打开时,必须执行某些 SQL 更新。我遇到了“数据库已锁定”异常。

SQLite 文档指出:

当进程想要从数据库文件中读取数据时,它遵循以下步骤顺序:

  1. 打开数据库文件并获得一个共享锁。

该文档进一步说明了“共享”锁定:

数据库可以被读取但不能被写入。任意数量的进程可以同时持有 SHARED 锁,因此可以有许多同时读取者。但是当一个或多个共享锁处于活动状态时,不允许其他线程或进程写入数据库文件。

常见问题解答指出:

多个进程可以同时打开同一个数据库。多个进程可以同时执行 SELECT。但是,任何时候只有一个进程可以对数据库进行更改。

《SQLite 权威指南》一书指出:

...通过使用read_uncommited pragma ,连接可以选择具有未提交读隔离级别。如果设置为true,则连接不会在它读取的表上设置读锁。因此,另一个写入者实际上可以更改表,因为处于未提交读模式的连接既不会阻塞也不会被任何其他连接阻塞。

我试图将编译指示设置为在 SQL 查询命令语句中读取未提交,如下所示:

PRAGMA read_uncommitted = 1;
SELECT Column1, Column2 FROM MyTable

使用不同连接在同一线程上进行 SQL 更新仍然失败,并出现“数据库已锁定”异常。然后我尝试将隔离级别设置为在连接实例上读取未提交。仍然没有变化,同样的例外。

如何让开放数据阅读器在不锁定数据库的情况下循环遍历数据库中的行,以便执行更新?

更新:

下面的两个答案都有效。然而,我已经从使用默认回滚日志转移到现在使用预写日志记录,它提供了改进的数据库读取和写入并发性。

4

2 回答 2

8

使用WAL模式。

于 2011-03-20T19:45:13.090 回答
3

我无法使用此处的开源数据提供程序使其正常工作。但是,我能够使用免费的标准版dotConnect使其工作,如下所示:

创建下面的 DLL 导入,以便我们可以为 SQLite 启用共享缓存。

[DllImport("sqlite3.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_enable_shared_cache(int enable);

执行上述函数启用共享缓存。请注意,整个过程只需执行一次 - 请参阅SQLite 文档

sqlite3_enable_shared_cache(1);

然后在数据读取器使用的 SQL 查询语句前面加上 pragma 语句,如下所示:

PRAGMA read_uncommitted = 1;
SELECT Column1, Column2 FROM MyTable

现在可以在数据读取器处于活动状态时自由更新和插入行。可以在此处找到有关共享缓存的其他 SQLite 文档。

更新:

Devart SQLite 数据提供程序的较新版本现在以改进的方式支持这一点。要启用共享缓存,可以进行以下调用:

Devart.Data.SQLite.SQLiteConnection.EnableSharedCache();

可以将未提交的读取配置到连接字符串中,例如如下:

Devart.Data.SQLite.SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder();
builder.ReadUncommitted = true;
builder.DateTimeFormat = Devart.Data.SQLite.SQLiteDateFormats.Ticks;
builder.DataSource = DatabaseFilePath;
builder.DefaultCommandTimeout = 300;
builder.MinPoolSize = 0;
builder.MaxPoolSize = 100;
builder.Pooling = true;
builder.FailIfMissing = false;
builder.LegacyFileFormat = false;
builder.JournalMode = JournalMode.Default;
string connectionString = builder.ToString();
于 2011-03-21T21:14:10.600 回答