SQLite事务如何使用?BEGIN、COMMIT、ROLLBACK全解析

来源:AI教程网作者:美园和花头衔:网络博主
导读:本期聚焦于美园和花创作的《SQLite事务如何使用?BEGIN、COMMIT、ROLLBACK全解析》,敬请观看详情。如果一条更新语句执行到一半时程序崩溃了,已经写入的数据会不会留在数据库里?这正是SQLite事务机制要解决的核心问题。事务将多条SQL语句包装成一个不可分割的执行单元,只有显式提交后修改才会真正落盘。本文从BEGIN、COMMIT、ROLLBACK三个命令切入,说明事务的开启、提交和回滚流程,并结合SQLite命令行和Python编程接口给出可运行的示例。还会讨论自动提交模式、SAVEPOINT的局部回滚能力以及事务与锁的交互。读完这篇文章,你可以理解如何用事务保证数据一致性,避免出现半更新状态。

当一条更新操作执行到一半时,如果系统掉电或者程序异常退出,数据库会不会留下不完整的数据?这个问题的答案取决于你是否使用了事务。SQLite中的事务可以把多条SQL语句组织成一个原子单元,要么全部生效,要么全部撤销,从而保证数据状态始终一致。事务控制的核心只有三个命令:BEGIN、COMMIT和ROLLBACK,但围绕它们展开的细节却不少。

SQLite事务如何使用?BEGIN、COMMIT、ROLLBACK全解析

SQLite默认运行在自动提交模式下,每一条单独的INSERT、UPDATE或DELETE语句都会在一个自动开启并提交的隐式事务中执行。只有显式使用BEGIN命令,后续的SQL语句才会被归入同一个事务,直到遇到COMMIT或ROLLBACK。理解这一点,是掌握SQLite事务的第一步。

SQLite事务的基本概念与ACID特性

事务是数据库管理系统中的一个逻辑工作单元,由一组SQL语句组成。SQLite支持标准的事务语义,并通过ACID四个特性来提供可靠性。原子性(Atomicity)确保事务中的操作要么全部完成,要么全部不发生;一致性(Consistency)保证事务完成后数据库从一个有效状态转变为另一个有效状态;隔离性(Isolation)让并发事务之间互不干扰;持久性(Durability)则意味着已提交的修改在掉电或崩溃后仍然存在。

在SQLite内部,原子性和持久性主要通过回滚日志(rollback journal)或预写式日志(WAL)来实现。当你在事务中执行写入操作时,SQLite会先把原始数据记录到日志中,只有收到COMMIT命令后才会把修改同步到主数据库文件。如果中途执行ROLLBACK或进程崩溃,SQLite可以根据日志内容恢复原状。这种机制让SQLite在嵌入式场景下也能具备接近大型数据库的事务能力。

值得注意的是,自动提交模式下的每一条语句也是一个事务,只不过事务边界由SQLite自动管理。对于多步骤业务操作,必须手动开启事务才能把多个SQL语句组合成一个不可分割的单元,否则一旦中途失败,之前已经执行的部分修改就会被永久保留下来。

BEGIN、COMMIT、ROLLBACK命令实战

三个命令的语法非常简单:BEGIN用于开启新事务,COMMIT用于提交事务,ROLLBACK用于回滚事务。在SQLite命令行工具sqlite3中,可以直接输入这些命令。下面用一个转账场景来演示它们的实际效果。

假设账户表中有两个用户,初始余额各为100。先执行一次转账:从A转50给B。这个操作包含两条UPDATE语句。如果第一条执行成功、第二条执行失败,没有事务保护时A的余额会减少而B的余额不变,造成数据不一致。把这两条UPDATE放在同一个事务里,就可以通过回滚避免这种半更新状态。

-- 创建账户表并插入初始数据
CREATE TABLE accounts (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  balance REAL NOT NULL
);

INSERT INTO accounts (name, balance) VALUES ('A', 100);
INSERT INTO accounts (name, balance) VALUES ('B', 100);

-- 开启事务
BEGIN;

UPDATE accounts SET balance = balance - 50 WHERE name = 'A';
UPDATE accounts SET balance = balance + 50 WHERE name = 'B';

-- 先回滚,观察数据是否恢复
ROLLBACK;

SELECT * FROM accounts;

执行上面的脚本后,查询结果会发现A和B的余额仍然是100,说明两条UPDATE都被撤销了。如果把ROLLBACK替换为COMMIT,则修改会被永久写入数据库。这种可控性正是事务存在的价值。

还要注意,在交互式命令行中,如果某条语句因为语法错误或约束失败而报错,SQLite并不会自动回滚整个事务。需要在应用层捕获错误并决定是继续执行还是回滚,否则事务可能一直处于未提交状态,占用锁资源。

在Python中使用SQLite事务

实际开发中很少直接手敲BEGIN命令,更多是通过编程语言驱动来控制事务。Python内置的sqlite3模块提供了事务管理接口。连接对象默认启用事务,只有在调用commit()后修改才会提交。如果在执行过程中出现异常,可以调用rollback()撤销未提交的修改。

来看一个Python版本的转账示例。代码先连接数据库,然后执行两条UPDATE,最后提交。如果任一UPDATE失败,except块会调用rollback()恢复原状。

import sqlite3

def transfer_money(conn, from_user, to_user, amount):
    try:
        cur = conn.cursor()
        cur.execute('UPDATE accounts SET balance = balance - ? WHERE name = ?', (amount, from_user))
        # 模拟一个可能因余额不足或约束导致的异常
        if amount > 100:
            raise ValueError('金额超出限制')
        cur.execute('UPDATE accounts SET balance = balance + ? WHERE name = ?', (amount, to_user))
        conn.commit()
        print('转账成功')
    except Exception as e:
        conn.rollback()
        print(f'转账失败,已回滚:{e}')

conn = sqlite3.connect('test.db')
# 需要在连接后确保表存在,此处省略建表步骤
transfer_money(conn, 'A', 'B', 50)
conn.close()

Python的sqlite3模块还提供了上下文管理器。使用with conn:语句时,如果代码块正常结束会自动提交,如果抛出异常则会自动回滚。这种方式更简洁,也能避免遗漏提交或回滚调用。

import sqlite3

conn = sqlite3.connect('test.db')
try:
    with conn:
        cur = conn.cursor()
        cur.execute('UPDATE accounts SET balance = balance - 50 WHERE name = \'A\'')
        cur.execute('UPDATE accounts SET balance = balance + 50 WHERE name = \'B\'')
except Exception as e:
    print(f'操作失败:{e}')

其他编程语言也遵循类似模式。例如Java的JDBC中Connection.setAutoCommit(false)会关闭自动提交,之后需要手动调用commit()或rollback()。Go的database/sql包则通过Tx类型来封装事务操作。无论哪种语言,核心思想都是一致的:把多条SQL语句放入同一个事务,由代码控制提交或回滚。

SAVEPOINT与部分回滚

除了整体回滚,SQLite还支持SAVEPOINT命令,允许在事务内部设置命名保存点。当执行ROLLBACK TO savepoint_name时,只有保存点之后的修改被撤销,事务本身仍然保持活动状态。这对于复杂业务流程非常有用,比如在一个大事务中执行多步操作,某一步失败后不必放弃全部工作,而只需回退到上一个稳定状态。

保存点的语法同样简单:SAVEPOINT sp1; 设置保存点sp1,然后执行若干SQL。如果需要回退,执行ROLLBACK TO sp1;,最后仍然需要COMMIT或RELEASE sp1;来结束保存点。保存点可以多个并存,也可以嵌套,但会占用一定内存和日志空间。

BEGIN;

-- 设置保存点
SAVEPOINT before_update;

UPDATE accounts SET balance = balance - 200 WHERE name = 'A';
UPDATE accounts SET balance = balance + 200 WHERE name = 'B';

-- 假设第二条更新失败,回退到保存点
ROLLBACK TO before_update;

-- 事务仍然可用,可以继续其他操作
UPDATE accounts SET balance = balance - 10 WHERE name = 'A';

COMMIT;

这段代码中,ROLLBACK TO before_update不会关闭事务,只是把数据库状态恢复到保存点创建时的样子。之后的UPDATE仍然被包含在事务中,最终由COMMIT提交。理解保存点和事务的关系,可以让你更精细地控制回滚范围。

事务与并发锁的关系

SQLite使用文件锁来协调多个连接对数据库的并发访问。事务的开启方式会影响锁的获取时机。默认的BEGIN等同于BEGIN DEFERRED,它不会在事务开始时获取任何锁,直到第一次实际执行读或写操作时才获取相应的共享锁或保留锁。这种延迟加锁有利于提高并发性,但也可能导致死锁。

另外两种模式是BEGIN IMMEDIATE和BEGIN EXCLUSIVE。BEGIN IMMEDIATE在事务开始时就获取保留锁,可以防止其他连接写入,但允许读取。BEGIN EXCLUSIVE则直接获取排他锁,阻止其他连接进行读写。在需要避免写冲突的批量更新场景中,选择合适的开启模式能减少database is locked错误。

写事务在提交时需要将保留锁升级为排他锁,如果此时有其他连接持有共享锁,SQLite会等待或者返回SQLITE_BUSY错误。可以通过busy_timeout参数设置等待时间。理解锁机制后,你就可以合理控制事务的粒度,避免长时间持有写锁影响其他读写操作。

SQLite事务BEGIN COMMIT ROLLBACK事务回滚修改时间:2026-09-23 06:44:07

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