如何在不锁定数据库的情况下使用数据读取器执行SQLite查询?

问题描述:

我正在使用System.Data.Sqlite访问C#中的SQLite数据库。我有一个查询必须通过表中的行读取。在遍历行时以及阅读器打开时,必须执行某些SQL更新。我遇到了“数据库被锁定”异常。如何在不锁定数据库的情况下使用数据读取器执行SQLite查询?

SQLite documentation状态:

当一个进程需要从数据库中读取文件,它遵循的步骤顺序如下:

  1. 打开数据库文件,并获得共享锁。关于 “共享” 锁定

的文件进一步指出:

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

FAQ状态:

多个进程可以同时打开同一个数据库。多个进程可以同时做一个SELECT。但是,只有一个过程可以随时对数据库进行更改。

The Definitive Guide to SQLite状态:

...的连接可以选择使用read_uncommited编译一个读未提交隔离级别。如果它设置为true,那么连接将不会在它读取的表上放置读锁定。因此,另一个写入程序实际上可以更改表,因为处于只读未提交模式下的连接既不能阻止也不能被任何其他连接阻止。

我试图设置编译阅读SQL查询命令语句内未提交如下:

PRAGMA read_uncommitted = 1; 
SELECT Column1, Column2 FROM MyTable 

使用还是失败,一个“数据库不同的连接在同一个线程上的SQL更新被锁定“例外。然后我试图将连接实例上的隔离级别设置为未提交读取。同样的例外情况仍然没有改变。

我该如何完成让一个开放的数据读取器循环遍历数据库中的行而不锁定数据库,以便我可以执行更新?

更新:

以下工作这两个答案。然而,我已经从使用默认回滚日志到现在使用Write-Ahead Logging,它提供了改进的数据库读取和写入并发性。

使用WAL模式。

+0

我使用ADO.Net。据http://sqlite.phxsoftware.com/ System.Data.SQLite.dll的版本1.0.66.0 2010年4月18日和SQLite的版本为3.6.23.1。看来我需要3.7+版本才能使用WAL。有什么建议么?是否有更新的ADO.Net提供程序? – Elan 2011-03-20 21:33:28

+0

是的,请参阅http://system.data.sqlite.org/index.html/doc/trunk/www/index.wiki 2011年2月版本1.0.68.0是与SQLite代码合并3.7.5 – 2011-03-21 02:35:24

+0

它看起来很有前途;不幸的是,我没有看到任何东西准备好或可供下载...是否提供官方提供的1.0.68.0版本,还是这是一个alpha/beta版本? – Elan 2011-03-21 17:39:34

我无法使用来自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); 

然后前缀使用的数据读取器与编译语句SQL查询语句如下:

PRAGMA read_uncommitted = 1; 
SELECT Column1, Column2 FROM MyTable 

之一,而在数据读取器处于活动状态现在可以*地更新和插入行。有关共享缓存的其他SQLite文档可以在here找到。

更新:

的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();