SQLite如何防止延迟事务的死锁?
根据关于递延赎金的规定: 默认事务行为被延迟。(…)对数据库的第一次读取操作将创建共享锁和 第一次写入操作创建一个保留锁 此外,根据打开的锁: 任意数量的进程可以同时持有共享锁(…) 一次只能激活一个保留锁,尽管有多个 共享锁可以与单个保留锁共存 这听起来像是带有任意读写器升级机制的多读写器/单写器锁,这是一种已知的死锁危险:SQLite如何防止延迟事务的死锁?,sqlite,Sqlite,根据关于递延赎金的规定: 默认事务行为被延迟。(…)对数据库的第一次读取操作将创建共享锁和 第一次写入操作创建一个保留锁 此外,根据打开的锁: 任意数量的进程可以同时持有共享锁(…) 一次只能激活一个保留锁,尽管有多个 共享锁可以与单个保留锁共存 这听起来像是带有任意读写器升级机制的多读写器/单写器锁,这是一种已知的死锁危险: 开始交易 B开始交易 获取共享锁并读取某些内容 B获取共享锁并读取某些内容 A获得保留锁并准备写入某些内容。只要存在其他共享锁,它就无法写入,因此会阻塞 B希望写,所以
- 开始交易
- B开始交易
- 获取共享锁并读取某些内容
- B获取共享锁并读取某些内容
- A获得保留锁并准备写入某些内容。只要存在其他共享锁,它就无法写入,因此会阻塞
- B希望写,所以尝试使用保留锁。已经有另一个保留锁,因此它会阻塞,直到释放为止,但仍保留共享锁
- 僵局
- 可能是写入程序在获取保留前释放共享锁。这将打破读写之间的原子性
- B在尝试获取保留锁时不阻塞,但会出错。这意味着所有的读取都需要重复,这会使API的使用变得非常复杂
我错过什么了吗?SQLite如何处理这个问题?为什么这种看似危险的事务会成为默认类型?通过简单的尝试和错误,我发现他们排除了错误 在给定的场景中,当B尝试保留时,它将首先等待
PRAGMA busy\u timeout
毫秒。然后它将报告错误:数据库已锁定
。事务仍将处于活动状态,因此可以立即重试
如果AFTER尝试提交(或者如果内存缓存中的内存不足),它将获取挂起的锁(防止额外的共享锁),然后等待独占。如果在PRAGMA busy_timeout
毫秒后仍有一些共享锁,它将报告错误:数据库已锁定。事务仍将处于活动状态,因此可以立即重试
换句话说,使用的死锁预防机制是超时。但是,它确实需要API用户通过回滚并重试来进行合作
作为一项准则:
- 当您只希望阅读时,只需使用
(或明确地使用begintransaction
)。写入可能会失败,迫使您回滚并再次重试整个事务begindeferredtransaction
- 当您希望在某个时候进行写操作时,请使用
立即开始事务处理。这将阻止所有其他编剧和所有其他直接编剧
将立即阻止,直到释放所有其他锁。我不知道为什么会有人想要这个。可能是为了准备一些数据,这些数据需要在到达后尽快写入磁盘编辑:这似乎是防止事务开始后在任意点超时的唯一方法开始独占事务