首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >SQLite如何防止延迟事务的死锁?

SQLite如何防止延迟事务的死锁?
EN

Stack Overflow用户
提问于 2019-04-24 13:47:53
回答 1查看 1.2K关注 0票数 2

根据文档关于延迟赎回的规定:

默认事务行为被推迟。(...)对数据库的第一读操作创建共享锁,第一写入操作创建保留锁。

此外,根据锁上的文档

任意数量的进程都可以同时持有共享锁(.)一次可能只有单个保留锁处于活动状态,尽管多个共享锁可以与单个保留锁共存。

这听起来像是具有任意读取器到写入器的多个读取器/单个写入器锁,这是已知的死锁危险:

  • A启动事务
  • B开始交易
  • 获取共享锁并读取某些内容。
  • B获取共享锁并读取某些内容
  • 一个获得保留锁并准备写一些东西。它不能写,只要有其他共享锁,所以它会阻塞。
  • B想写,所以试着取下预定锁。已经有了另一个保留锁,所以它一直阻塞,直到释放为止,仍然持有共享锁。
  • 死锁。

那么SQLite是如何绕过这个问题的呢?我想到了两种可能的解决方案,但这两种解决方案似乎都打破了整个交易的想法:

  • 可能的写入者在获得保留之前释放共享锁。这将打破读写之间的原子性。
  • B在尝试使用预留锁时不会阻塞,但会出错。这意味着所有的读取都需要重复,并且会使API的使用变得非常复杂。

我是不是遗漏了什么?SQLite是如何处理这个问题的?为什么这种看似危险的交易是默认的呢?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-04-24 15:49:21

通过简单的尝试和错误,我发现他们走了错误的路线.

在给定的场景中,当B尝试保留时,它将首先等待PRAGMA busy_timeout毫秒。然后它将报告Error: database is locked。事务仍将处于活动状态,因此可以立即重试。

如果A随后尝试COMMIT (或者如果内存中缓存用完了),它将接受挂起的锁(防止额外的共享锁),然后等待独占。如果某些共享锁在PRAGMA busy_timeout毫秒后仍然存在,它将报告错误:数据库被锁定。事务仍将处于活动状态,因此可以立即重试。

换句话说,所使用的死锁预防机制是超时。但是,它确实要求API用户通过回滚和再次尝试来进行协作。

作为指导方针:

  • 只使用BEGIN TRANSACTION (或显式BEGIN DEFERRED TRANSACTION)当您只希望阅读。写入可能会失败,迫使您回滚并再次重试整个事务。
  • 当您希望在某个时候编写时,请使用BEGIN IMMEDIATE TRANSACTION。这将阻止所有其他作家和所有其他直接的作家。
  • BEGIN EXCLUSIVE TRANSACTION将立即阻塞,直到释放所有其他锁为止。我不知道为什么会有人想要这个。可能是为了准备一些需要在数据到达磁盘后尽快写入磁盘的数据?编辑:,这似乎是在事务开始后防止任意点超时的唯一方法。
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55831645

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档