如果我有多个数据库读取器,它们基本上是以“读取”模式打开数据库文件的 Sqlite 实例,那么对于以“创建”模式打开数据库的单个写入器来说,在所有读取器关闭数据库之前无法释放事务,这正常吗?
我试图弄清楚这是 sqlite 的正常行为,还是我正在使用的特定 sqlite 库的错误/限制。
我不明白为什么作者需要等待读者停止使用数据库文件,这对我来说没有意义,因为读者无法更改数据
如果我有多个数据库读取器,它们基本上是以“读取”模式打开数据库文件的 Sqlite 实例,那么对于以“创建”模式打开数据库的单个写入器来说,在所有读取器关闭数据库之前无法释放事务,这正常吗?
我试图弄清楚这是 sqlite 的正常行为,还是我正在使用的特定 sqlite 库的错误/限制。
我不明白为什么作者需要等待读者停止使用数据库文件,这对我来说没有意义,因为读者无法更改数据
不可以。在所有读者退出数据库之前,你的写入器无法释放事务,这是不正常的。
这就引出了一个问题:你究竟如何打开数据库文件?
sqlite 引擎确实有以只读模式打开数据库的能力: https://www.sqlite.org/c3ref/open.html
但是默认标志(第三个参数)是
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
。如果有一个应用程序以正常方式打开文件
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
。并且有多个应用程序以模式打开SQLITE_OPEN_READONLY
,则写入器不应等待读取器。它甚至不知道它们。sqlite3
如果可以,请尝试使用控制台工具进行查询。它有一个--readonly
选项,可以真正以只读模式打开数据库文件。但是,如果您不是使用 C 语言,而是使用某种语言绑定,则文件可能会始终正常打开,然后在绑定级别切换到只读模式。
从形式上看,该
select
语句可以对表模式进行锁定,这样当一个用户alter table
正在从表中进行选择时,另一个用户就无法执行任何操作。但这是读者可以对表进行锁定的唯一真正方法。sqlite
只读模式下的唯一锁是为了防止数据库文件被删除。阅读有关共享缓存模式的文章也很有帮助:https://www.sqlite.org/sharedcache.html在某些情况下可以做到这一点,并且如果事务基于相同的缓存,它还可以干扰事务。
因为每个 SQLite 事务(隐式或显式)都会锁定数据库文件。
您可以通过在连接到同一 SQLite 数据库文件的两个不同终端会话中运行 SQLite3 来测试这一点。确切的行为取决于几个细节,尤其是“预写日志”(WAL) 选项。再次参见事务
写入者不会“释放”事务;它们要么提交,要么回滚。例如,如果写入者试图提交插入语句,而另一个进程打开了一个选择事务(即正在“读取数据库”),则写入者会在我的计算机上收到此错误。
除了 Mike 所说的之外,关于您的想法:
当然,但是写入者确实会改变数据库,并且如果读取者正在读取数据并且写入者更改了其中的一些数据,则可能会出现读取者无法预料的结果。
例如,如果读取查询正在扫描索引中的每一行,那么对于想要更新键值的写入者来说,如果该键值会改变该索引中行的位置,那么必须等到读取者完成扫描索引才有意义。否则,读取者可能会两次重新读取同一数据行,并返回不准确的结果。这称为不可重复读取。
可能发生的另一个问题是脏就绪,即读取器尚未完成扫描索引,写入器对某一行进行了更改,然后读取器读取该行,而写入器的事务被回滚(撤消/未提交),导致读取器读取的数据从未保存到数据库中,从而导致不准确。
这些非常简单的示例是为什么除了写入器也会阻塞读取器之外,读取器通常会阻塞写入器的一些原因。存在隔离级别和乐观并发的概念,允许写入器和读取器在同时操作同一数据时不相互阻塞。但是,使用此类功能的能力在每个数据库系统的实现中有所不同,我不确定 SQLite 是否提供了此功能。