我正在使用System.Data.Sqlite访问C#中的SQLite数据库。我有一个查询必须读取表中的行。在遍历行并且在阅读器打开时,必须执行某些SQL更新。我遇到了“数据库被锁定”的异常。
当进程想要从数据库文件中读取时,它遵循以下步骤:
- 打开数据库文件并获取SHARED锁。
醇>
文档进一步说明了“共享”锁定:
可以读取数据库但不能写入。任意数量的进程可以同时保存SHARED锁,因此可以有许多同时读取器。但是,当一个或多个SHARED锁处于活动状态时,不允许其他线程或进程写入数据库文件。
FAQ州:
多个进程可以同时打开同一个数据库。多个进程可以同时执行SELECT。但是,只有一个进程可以随时对数据库进行更改。
这本书The Definitive Guide to SQLite陈述:
...通过使用 read_uncommited pragma,连接可以选择具有 read-uncommitted 隔离级别。如果设置为 true ,则连接不会对其读取的表放置读锁定。因此,另一个编写器实际上可以更改表,因为读取未提交模式下的连接既不会阻塞也不会被任何其他连接阻塞。
我尝试将pragma设置为在SQL查询命令语句中读取未提交,如下所示:
PRAGMA read_uncommitted = 1;
SELECT Column1, Column2 FROM MyTable
使用不同连接的同一线程上的SQL更新仍然因“数据库已锁定”异常而失败。然后,我尝试将隔离级别设置为在连接实例上读取未提交。同样的例外情况仍然没有变化。
如何在不锁定数据库的情况下使用开放数据读取器来遍历数据库中的行,以便我可以执行更新?
更新
以下两个答案都有效。但是,我已经从使用默认回滚日志转移到现在使用预写日志,这提供了改进的数据库读写并发性。
答案 0 :(得分:8)
使用WAL模式。
答案 1 :(得分:2)
我无法使用here中的开源数据提供程序来实现此功能。但是,我能够使用dotConnect的免费标准版如下工作:
创建以下DLL导入,以便我们可以为SQLite启用共享缓存。
[DllImport("sqlite3.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_enable_shared_cache(int enable);
执行上述功能以启用共享缓存。请注意,这只需要对整个过程执行一次 - 请参阅SQLite documentation。
sqlite3_enable_shared_cache(1);
然后使用pragma语句为数据读取器使用的SQL查询语句添加前缀,如下所示:
PRAGMA read_uncommitted = 1;
SELECT Column1, Column2 FROM MyTable
现在可以在数据读取器处于活动状态时自由更新和插入行。可以在here找到有关共享缓存的其他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();