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

发布于 2024-10-25 05:17:11 字数 1587 浏览 1 评论 0原文

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

SQLite 文档 指出:

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

  1. 打开数据库文件并获取共享锁。

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

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

常见问题解答指出:

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

该书SQLite 权威指南 指出:

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

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

PRAGMA read_uncommitted = 1;
SELECT Column1, Column2 FROM MyTable

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

如何让开放式数据读取器在不锁定数据库的情况下循环访问数据库中的行,以便我可以执行更新?

更新:

下面的两个答案都有效。然而,从那以后我不再使用默认的回滚日志,而是现在使用预写日志记录,它提供了改进的数据库读写并发性。

I am using System.Data.Sqlite to access SQLite database in C#. I have a query which must read through rows in a table. While iterating through the rows and while the reader is open, certain SQL updates must be performed. I am running into a "database is locked" exception.

The SQLite documentation states:

When a process wants to read from a database file, it followed the following sequence of steps:

  1. Open the database file and obtain a SHARED lock.

The documentation further states about "SHARED" locking:

The database may be read but not written. Any number of processes can hold SHARED locks at the same time, hence there can be many simultaneous readers. But no other thread or process is allowed to write to the database file while one or more SHARED locks are active.

The FAQ states:

Multiple processes can have the same database open at the same time. Multiple processes can be doing a SELECT at the same time. But only one process can be making changes to the database at any moment in time, however.

The book The Definitive Guide to SQLite states:

...a connection can choose to have a read-uncommitted isolation level by using the read_uncommited pragma. If it is set to true, then the connection will not put read locks on the tables it reads. Therefore, another writer can actually change a table as the connection in read-uncommitted mode can neither block nor be blocked by any other connections.

I attempted to set the pragma to read uncommitted within the SQL query command statement as follows:

PRAGMA read_uncommitted = 1;
SELECT Column1, Column2 FROM MyTable

A SQL update on the same thread using a different connection still failed with a "database is locked" exception. I then attempted to set the isolation level to read uncommitted on the connection instance. Still no change with the same exception.

How can I accomplish having an open data reader to loop through rows in the database without locking the database, so that I can execute updates?

Update:

Both answers below work. I have however since moved away from using the default rollback journal to now using the Write-Ahead Logging, which provides improved concurrency of database reads and writes.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

小清晰的声音 2024-11-01 05:17:11

使用WAL模式。

Use WAL mode.

橘香 2024-11-01 05:17:11

我无法使用此处的开源数据提供程序来使其正常工作。但是,我可以使用 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();

I was not able to get this to work using the open source data provider from here. However, I was able to get this to work using the free Standard Edition of dotConnect as follows:

Create the below DLL import, so that we can enable the shared cache for SQLite.

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

Execute the above function to enable the shared cache. Note, this only needs to be executed once for the entire process - see SQLite documentation.

sqlite3_enable_shared_cache(1);

Then prefix the SQL query statement used by the data reader with the pragma statement as follows:

PRAGMA read_uncommitted = 1;
SELECT Column1, Column2 FROM MyTable

One can now freely update and insert rows while the data reader is active. Additional SQLite documentation on the shared cache can be found here.

Update:

A newer version of the Devart SQLite data provider now supports this in an improved manner.
To enable the shared cache one can make the following call:

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

One can configure the uncommitted reads into the connection string for example as follows:

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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文