根据documentation关于延期的交易:
默认事务行为被推迟。 (...)对数据库的第一次读取操作将创建SHARED锁,并且 第一次写操作将创建一个RESERVED锁。
还要根据锁上的documentation:
任何数量的进程可以同时持有SHARED锁(...) 一次只能激活一个保留锁,尽管多个 共享锁可以与单个保留锁共存
这听起来像是具有任意读取器到写入器提升机制的多个读取器/单个写入器锁,这被认为是死锁危险:
那么SQLite如何解决这个问题?我想到了两种可能的解决方案,但是它们似乎都破坏了整个交易的想法:
我想念什么吗? SQLite如何处理呢?为什么将这种看似危险的交易类型设为默认交易?
答案 0 :(得分:0)
通过简单的尝试和错误,我发现他们采用了错误排除路线。
在给定的情况下,当B尝试获取RESERVED时,它将首先等待PRAGMA busy_timeout
毫秒。然后它将报告Error: database is locked
。该交易仍将处于活动状态,因此可以立即重试。
如果A之后尝试访问COMMIT
(或者它用完了内存缓存),它将获得PENDING锁(防止其他SHARED锁),然后等待EXCLUSIVE。如果在PRAGMA busy_timeout
毫秒后仍保留一些SHARED锁定,它将报告错误:数据库已锁定。该交易仍将处于活动状态,因此可以立即重试。
换句话说,正在使用的防止死锁机制是超时。但是,它确实要求API用户通过回滚并重试来进行合作。
作为准则:
BEGIN TRANSACTION
(或明确使用BEGIN DEFERRED TRANSACTION
)。写入可能会失败,从而迫使您回滚并再次重试整个事务。BEGIN IMMEDIATE TRANSACTION
。这将阻止所有其他作家和所有其他直接的也许作家。BEGIN EXCLUSIVE TRANSACTION
将立即阻塞,直到释放所有其他锁为止。我不知道为什么有人会想要这个。可能准备一些需要在数据到达后尽快写入磁盘的数据吗? 编辑:这似乎是防止在开始交易后任意时间超时的唯一方法。