Skip to content

SQLite 事务特性

事务概述

事务是数据库操作的基本单元,确保一组相关操作要么全部成功执行,要么全部回滚。SQLite 完全支持 ACID(原子性、一致性、隔离性、持久性)特性,为应用程序提供可靠的数据操作保障。

ACID 支持

原子性 (Atomicity)

SQLite 通过预写日志(WAL)和回滚日志机制确保事务的原子性。所有修改操作首先记录到日志文件,然后才应用到数据库文件。如果事务执行过程中发生故障,SQLite 可以通过日志文件恢复到事务开始前的状态。

一致性 (Consistency)

SQLite 确保事务执行前后数据库始终保持一致状态。通过约束检查(主键、外键、唯一约束等)和触发器机制,SQLite 自动验证数据完整性,防止无效数据进入数据库。

隔离性 (Isolation)

SQLite 支持多种事务隔离级别,控制并发事务之间的相互影响:

  • READ UNCOMMITTED:允许读取未提交的数据,可能导致脏读
  • READ COMMITTED:只能读取已提交的数据,避免脏读
  • REPEATABLE READ:确保同一事务中多次读取相同数据返回一致结果,避免不可重复读
  • SERIALIZABLE:最高隔离级别,确保事务串行执行,避免幻读

默认情况下,SQLite 使用 SERIALIZABLE 隔离级别,提供最强的隔离保障。

持久性 (Durability)

事务提交后,修改会永久保存到数据库文件。SQLite 通过同步机制(sync pragma)确保数据可靠写入磁盘,防止系统崩溃导致数据丢失。

事务控制语句

开始事务

sql
-- 显式开始事务
BEGIN TRANSACTION;
-- 或简化形式
BEGIN;

提交事务

sql
-- 提交事务,保存所有修改
COMMIT TRANSACTION;
-- 或简化形式
COMMIT;

回滚事务

sql
-- 回滚事务,撤销所有修改
ROLLBACK TRANSACTION;
-- 或简化形式
ROLLBACK;

保存点

SQLite 支持保存点功能,允许在事务内创建多个检查点,以便部分回滚:

sql
-- 开始事务
BEGIN;

-- 执行一些操作
INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');

-- 创建保存点
SAVEPOINT sp1;

-- 执行更多操作
UPDATE users SET email = 'alice.new@example.com' WHERE name = 'Alice';

-- 回滚到保存点,撤销保存点后的操作
ROLLBACK TO SAVEPOINT sp1;

-- 提交事务,只保存保存点前的操作
COMMIT;

事务隔离级别设置

设置隔离级别

sql
-- 设置为 READ COMMITTED 隔离级别
PRAGMA read_uncommitted = 0;
PRAGMA journal_mode = WAL;

查看当前隔离级别

sql
PRAGMA read_uncommitted;

事务最佳实践

1. 显式使用事务

虽然 SQLite 默认在自动提交模式下运行(每个语句都是一个事务),但显式使用事务可以提高性能并确保操作的原子性:

sql
-- 不推荐:每个语句单独事务
INSERT INTO users (name) VALUES ('User1');
INSERT INTO users (name) VALUES ('User2');
INSERT INTO users (name) VALUES ('User3');

-- 推荐:显式事务
BEGIN;
INSERT INTO users (name) VALUES ('User1');
INSERT INTO users (name) VALUES ('User2');
INSERT INTO users (name) VALUES ('User3');
COMMIT;

2. 保持事务简短

长时间运行的事务会占用数据库锁,影响并发性能。尽量将事务设计为短时间、小批量操作:

sql
-- 不推荐:长时间运行的大事务
BEGIN;
-- 执行大量插入、更新操作
COMMIT;

-- 推荐:批量提交
for batch in batches:
    BEGIN;
    -- 执行小批量操作
    COMMIT;

3. 合理使用保存点

对于复杂操作,使用保存点可以提供更细粒度的回滚控制:

sql
BEGIN;
-- 执行操作1
SAVEPOINT step1;
-- 执行操作2
SAVEPOINT step2;
-- 执行操作3

-- 如果操作3失败,回滚到step2
ROLLBACK TO SAVEPOINT step2;
-- 重新执行操作3
-- ...

COMMIT;

4. 避免在事务中执行耗时操作

不要在事务中执行文件I/O、网络请求等耗时操作,这会导致事务长时间持有锁:

sql
-- 不推荐:事务中包含耗时操作
BEGIN;
-- 更新数据库
UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = 1;
-- 发送邮件(耗时操作)
send_email('user@example.com', 'Login notification');
COMMIT;

-- 推荐:先更新数据库,再执行耗时操作
BEGIN;
UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = 1;
COMMIT;
-- 发送邮件

版本差异

SQLite 3.7.0 及以上

  • 引入了 WAL(Write-Ahead Logging)模式,显著提高了并发性能
  • 改进了事务处理机制,减少了锁争用

SQLite 3.8.0 及以上

  • 增强了保存点功能,支持嵌套事务
  • 优化了事务提交和回滚的性能

SQLite 3.25.0 及以上

  • 支持 UPSERT 语句,简化了条件插入更新操作
  • 改进了事务隔离级别的实现

常见问题(FAQ)

Q: SQLite 是否支持分布式事务?

A: 不,SQLite 是单文件数据库,不支持分布式事务。对于需要分布式事务的场景,应考虑使用其他数据库系统,如 PostgreSQL 或 MySQL。

Q: 如何提高事务性能?

A:

  • 使用 WAL 模式
  • 显式使用事务,减少事务次数
  • 保持事务简短
  • 合理设置 synchronous pragma(例如设置为 NORMAL 或 OFF)
  • 使用批量操作

Q: 事务回滚会影响自增主键吗?

A: 是的,SQLite 中的自增主键(AUTOINCREMENT)在事务回滚后不会重用已分配的值。这是设计使然,用于确保主键的唯一性。

Q: 如何处理长事务导致的锁争用?

A:

  • 将长事务拆分为多个短事务
  • 使用 WAL 模式减少读锁和写锁的冲突
  • 优化查询,减少事务执行时间
  • 考虑使用更低的隔离级别(如果业务允许)

Q: SQLite 支持嵌套事务吗?

A: 不完全支持。SQLite 通过保存点(savepoint)机制提供类似嵌套事务的功能,但与真正的嵌套事务有所不同。每个 BEGIN 语句都会开启一个新的事务,而保存点只在当前事务内有效。

生产运维建议

  1. 监控事务时长:使用 SQLite 内置的性能监控工具或第三方监控软件,监控长事务的执行情况
  2. 定期检查日志:关注数据库日志中的事务相关警告和错误
  3. 设置合理的同步级别:根据业务对数据安全性的要求,调整 synchronous pragma 设置
  4. 备份事务日志:确保 WAL 文件和回滚日志与数据库文件一起备份
  5. 测试故障恢复:定期进行故障恢复测试,验证事务的持久性和一致性

通过合理使用事务特性,可以确保 SQLite 数据库在各种场景下的可靠性和性能。