【SQLite 内核】锁状态与 shared cache:五态锁阶梯与 writer starvation
内容提要
本文介绍SQLite 3.0的锁状态机,五态为UNLOCKED、SHARED、RESERVED、PENDING、EXCLUSIVE。RESERVED允许新读者,PENDING拒绝新读者以防写者饿死。Pager仅跟踪四态,PENDING是瞬时状态。实测验证RESERVED唯一性,读写可共存。shared cache已过时,WAL模式另用独立锁机制。
延伸解读
PENDING 锁:写者饿死的工程解
SQLite 3.0 引入 PENDING 锁,直接针对 2.x 版本中写者可能因读者持续涌入而永远无法获得独占锁的问题。PENDING 状态拒绝新 SHARED 锁,但允许已有读者完成,从而在写者等待时阻止新读者进入,确保写者最终能升级到 EXCLUSIVE。这一设计简单有效,无需复杂调度,是解决读写锁公平性问题的经典范例。
RESERVED 与 PENDING 的实践差异
RESERVED 表示写意图,但允许新读者进入;PENDING 则拒绝新读者,只等待现有读者清空。实测中,一个连接持有 RESERVED 时,另一连接的写操作立即失败(SQLITE_BUSY),而读操作正常返回。这验证了 RESERVED 的全局唯一性及读写共存边界,帮助理解 SQLite 并发控制的实际行为。
Pager 为何只跟踪四态
Pager 模块仅跟踪 UNLOCKED、SHARED、RESERVED、EXCLUSIVE 四态,因为 PENDING 只是通往 EXCLUSIVE 的瞬时状态,由操作系统接口层处理。Pager 关注的是外部可观察的持久状态,PENDING 不改变 Pager 对上层暴露的状态视图,因此无需单独跟踪。这简化了 Pager 的状态机设计。
shared cache 的过时与边界
shared cache 模式曾用于同进程内多连接共享缓存,但官方已标记为过时,推荐使用 WAL 模式。其表级锁与文件级锁并存,但仅在同进程内生效。理解这一边界可避免误以为 shared cache 能替代文件级锁,或将其等同于跨进程共享缓冲池。
Q&A
SQLite 3.0 的五种锁状态分别是什么?它们之间如何共存?
SQLite 3.0 的五种锁状态是 UNLOCKED、SHARED、RESERVED、PENDING 和 EXCLUSIVE。UNLOCKED 表示未持有任何锁;SHARED 可读不可写,允许多个进程同时持有;RESERVED 表示计划写但仍在读,全局唯一且允许新的 SHARED 锁;PENDING 表示等待升级为 EXCLUSIVE,拒绝新的 SHARED 锁但允许已有的 SHARED 锁继续;EXCLUSIVE 表示正在写文件,完全独占。
SQLite 中 RESERVED 锁和 PENDING 锁有什么区别?
RESERVED 锁和 PENDING 锁的关键区别在于是否允许新的 SHARED 锁。RESERVED 锁表示进程打算写,但允许新的 SHARED 锁获取;而 PENDING 锁表示进程即将写,拒绝新的 SHARED 锁,只允许已有的 SHARED 锁释放。两者都全局唯一,但对新读者的态度相反。
SQLite 的写操作是如何依次经过五态锁的?
写操作先获取 SHARED 锁,然后升级为 RESERVED 锁,创建 rollback journal 并写入原始页。当缓存满或提交时,申请 PENDING 锁,等待现有 SHARED 锁清空后升级为 EXCLUSIVE 锁,在 EXCLUSIVE 下写回脏页并删除 journal,最后释放锁。
为什么 Pager 模块只跟踪四种锁状态而不跟踪 PENDING?
Pager 模块只跟踪 UNLOCKED、SHARED、RESERVED、EXCLUSIVE 四种状态,因为 PENDING 只是通往 EXCLUSIVE 的瞬时状态,是操作系统锁接口层的实现细节,用于防止写者饿死。Pager 关心的是外部可观察的持久状态,而 PENDING 不需要长期维护。
SQLite 的 shared cache 模式是什么?为什么说它已经过时?
shared cache 模式允许多个连接共享同一份内存缓存和 schema,并增加事务级、表级、schema 级锁来协调访问。它最初为 Symbian 手机设计,但官方已将其标记为过时,因为 WAL 模式提供了更好的读写并发,且不牺牲事务隔离。
SQLite 的锁机制如何解决 writer starvation 问题?
SQLite 3.0 通过引入 PENDING 锁解决 writer starvation。在写者等待升级为 EXCLUSIVE 时,PENDING 锁拒绝新的 SHARED 锁,但允许已有的 SHARED 锁释放,从而防止读者持续涌入导致写者永远无法获得独占锁。
SQLite 的锁机制在哪些场景下可能失效?
SQLite 的锁机制可能失效的场景包括:读连接长期不释放导致 PENDING 锁无限期等待;网络文件系统上 POSIX advisory locking 可能不可靠。官方建议不要在网络文件系统上运行 SQLite。