导读:本期聚焦于小伙伴创作的《SQLite中DEFERRED、IMMEDIATE、EXCLUSIVE事务到底有什么区别?》,敬请观看详情。写并发程序时数据库锁冲突常常让人头疼。SQLite提供DEFERRED、IMMEDIATE、EXCLUSIVE三种事务起始方式,核心差异在于加锁时机与范围。DEFERRED直到首次读写才获取锁,IMMEDIATE在事务开始就拿写锁阻止其他写者,EXCLUSIVE则独占数据库禁止任何并发访问。理解它们能帮你在避免死锁和保证性能之间做取舍。下文从底层锁状态、典型场景与代码写法展开说明,让你不再因选错事务类型而踩坑。

在SQLite里发起事务并不是简单地执行BEGIN就完事,紧跟其后的修饰符会直接决定数据库锁的获取时机与排斥范围。不少人在高并发写入时出现database is locked错误,根源往往就是没弄清楚DEFERRED、IMMEDIATE与EXCLUSIVE这三者的行为边界。本文从锁模型出发,把这三种事务类型的底层逻辑、使用场景和代码写法逐一拆开来讲。

SQLite中DEFERRED、IMMEDIATE、EXCLUSIVE事务到底有什么区别?

SQLite锁机制与事务起点的关系

SQLite在文件级实现了一套轻量锁体系,包含UNLOCKED、SHARED、RESERVED、PENDING和EXCLUSIVE五种状态。普通读操作只需SHARED锁,写操作则要先拿RESERVED,再经PENDING最终升级为EXCLUSIVE。事务起始修饰符的作用,就是提前规定什么时候去碰这些锁。

DEFERRED是默认模式,执行BEGIN DEFERRED时数据库完全不加任何锁,直到第一条SELECT才加SHARED,第一条INSERT或UPDATE才尝试RESERVED。这种延迟加锁降低了启动开销,但如果两个事务都 defer 到写阶段才抢锁,就容易在升级时互相阻塞甚至死锁。IMMEDIATE在BEGIN IMMEDIATE那一刻就直接拿RESERVED锁,等于提前占住写名额,别的事务再想写就会立即失败或等待,但读仍可进行。EXCLUSIVE则一上来就霸占EXCLUSIVE锁,期间任何连接都不能读也不能写。

从内核角度看,锁的获取越靠前,冲突暴露得越早,程序也越容易用重试逻辑兜底。理解这张锁时序表,是选对事务类型的前提。下面用一段C伪代码展示不同起点下的锁状态变化:

// 假设db为已打开的sqlite3句柄
sqlite3_exec(db, "BEGIN DEFERRED", 0, 0, 0); // 此时无锁
sqlite3_exec(db, "SELECT * FROM t", ...);    // 加SHARED锁
sqlite3_exec(db, "UPDATE t SET v=1", ...);   // 尝试RESERVED->EXCLUSIVE

sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, 0); // 直接RESERVED锁
sqlite3_exec(db, "UPDATE t SET v=2", ...);    // 后续可平滑升级

sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); // 直接EXCLUSIVE锁
// 此时其他连接连SELECT都会被拒

三种事务类型的典型应用场景对比

选DEFERRED适合读多写少且写逻辑靠后的场景,比如先跑一堆查询确认数据存在,再决定要不要插入。因为它的锁是按需出现,短时间重叠写的概率低。但如果你明确知道接下来一定要写,还用DEFERRED,就等于把冲突留到执行中途,那时已经做了部分计算,回滚成本更高。

IMMEDIATE常见于“先占坑再慢慢改”的批处理。例如后台脚本要汇总多个表后回写,用BEGIN IMMEDIATE能在启动时就确保自己有写权,避免做到一半被别的进程插队导致database is locked。它不阻塞读,对线上读服务影响小,是多数写任务的安全默认。EXCLUSIVE则用于结构性变更,比如VACUUM、大量删表重建,或者测试里要绝对隔离。此时宁可让全库不可访问几秒,也不想被并发读拖慢写升级。

我们用一个简表归纳差异:

类型起始加锁排斥读排斥写适用情形
DEFERRED无(延迟)写升级时读后偶写
IMMEDIATERESERVED明确写任务
EXCLUSIVEEXCLUSIVE维护/隔离

实际项目中,如果业务代码用ORM,往往默认走DEFERRED,需要显式写BEGIN IMMEDIATE才能改。忽视这层,就会在并发稍高时频繁重试。

代码写法与避坑实践

在SQLite命令行或编程接口里,修饰符紧跟BEGIN。写错顺序如BEGIN TRANSACTION IMMEDIATE也是合法的,但BEGIN DEFERRED可省略DEFERRED。Python的sqlite3模块默认不暴露修饰符,需要靠execute("BEGIN IMMEDIATE")手动发指令,而不是用默认commit前的隐式事务。

一个常见坑是:在DEFERRED事务里先读后写,另一个连接也这么干,两者都拿着SHARED,然后都要升EXCLUSIVE,谁也不让谁,SQLite会检测死锁并让其中一个失败。此时应统一改成IMMEDIATE,让先到者占RESERVED,后到者立刻知道没戏,重试即可。下面给出Python正确写法:

import sqlite3
conn = sqlite3.connect("app.db")
try:
    # 显式使用IMMEDIATE避免延迟写锁冲突
    conn.execute("BEGIN IMMEDIATE")
    row = conn.execute("SELECT cnt FROM counter WHERE id=1").fetchone()
    new_cnt = row[0] + 1
    conn.execute("UPDATE counter SET cnt=? WHERE id=1", (new_cnt,))
    conn.execute("COMMIT")
except sqlite3.OperationalError as e:
    conn.execute("ROLLBACK")
    print("事务冲突,可重试:", e)

另外注意,EXCLUSIVE在WAL模式下行为略有不同,它仍允许其他连接读最近提交版本,但禁止新写。若你用PRAGMA journal_mode=WAL,别误以为EXCLUSIVE能完全冻结节点的读。掌握这些细节,才能在延迟与隔离之间调到合适平衡点。

SQLite事务隔离锁机制修改时间:2026-08-13 12:30:36

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。