结构化查询语言(Structured Query Language)
事务的概念
事务是由一个或多个 SQL 语句组成的逻辑操作单元,包括对数据库的写操作(如 INSERT
、UPDATE
、DELETE
),以及可以包含的查询语句(如 SELECT
)。事务的主要目的是保证数据库从一个一致的状态转换到另一个一致的状态,即使在并发处理、磁盘故障或断电等关键情况下,也能维护数据库的完整性。
事务的核心是支持 ACID 属性,确保数据一致性和可靠性:
- 原子性 (Atomic):事务中的所有操作要么全部执行,要么全部撤销。
- 一致性 (Consistent):事务的所有操作使数据库从一个一致状态转换到另一个一致状态。
- 隔离性 (Isolated):事务的未提交部分对其他会话不可见,隔离级别定义了可见性规则。
- 持久性 (Durable):事务一旦提交,其结果永久保存在数据库中,即使系统崩溃也不会丢失。
事务边界
每个对数据库进行写操作的 SQL 语句都必须是事务的一部分。如果没有显式启动事务,DBMS 会为每条语句隐式地启动一个事务。如果需要显式控制,可以通过 START TRANSACTION
命令开始事务。
事务中的所有语句都会暂时存储在一个中间区域,直到提交(COMMIT
)或回滚(ROLLBACK
)为止:
COMMIT
:将事务的所有修改永久保存到数据库,并对其他会话可见。ROLLBACK
:撤销事务中的所有修改,并终止事务。
示例:
-- 显式启动事务(大多数情况下不是必须的,Oracle 不支持)
START TRANSACTION;
-- 插入数据
INSERT INTO table_name ...;
-- 修改数据
UPDATE table_name ...;
-- 删除数据
DELETE FROM table_name ...;
-- 提交事务,确认所有更改
COMMIT;
-- 另一个事务示例
START TRANSACTION;
INSERT INTO table_name ...;
UPDATE table_name ...;
DELETE FROM table_name ...;
-- 回滚事务,丢弃所有更改
ROLLBACK;
保存点 (Savepoints)
在事务中,某些场景可能需要只回滚部分操作,而非整个事务。通过定义 保存点 (Savepoints),可以创建事务的中间边界。保存点允许部分回滚事务,但提交(COMMIT
)或完整回滚(ROLLBACK
)会终止整个事务及其所有保存点。
使用保存点的示例:
-- 显式启动事务
START TRANSACTION;
-- 插入数据
INSERT INTO table_name ...;
-- 定义保存点
SAVEPOINT step_1;
-- 更新数据
UPDATE table_name ...;
-- 仅回滚到保存点,保留之前的插入操作
ROLLBACK TO SAVEPOINT step_1;
-- 重试更新或执行其他操作
UPDATE table_name ...;
-- 提交事务,确认插入和更新的最终结果
COMMIT;
释放保存点
在事务的生命周期中,可以显式释放不再需要的保存点。释放保存点不会影响事务中的操作结果,只是无法再回滚到该保存点。
-- 定义保存点
SAVEPOINT step_2;
-- 执行一些操作
INSERT INTO table_name ...;
UPDATE table_name ...;
-- 释放保存点
RELEASE SAVEPOINT step_2;
-- 注意:释放保存点后,无法再回滚到 `step_2`。
总结
-
事务管理:
- 通过
START TRANSACTION
显式启动事务; - 使用
COMMIT
提交更改; - 使用
ROLLBACK
撤销更改。
- 通过
-
保存点的作用:
SAVEPOINT
:在事务中定义中间回滚点;ROLLBACK TO SAVEPOINT
:回滚到指定保存点;RELEASE SAVEPOINT
:释放保存点,无法再回滚到该点。
事务和保存点的组合使用可以提供灵活的错误处理和数据管理机制,有助于维护数据库的一致性和完整性。
原子性 (Atomicity)
事务保证其所有语句的结果在逻辑上被视为一个整体操作。在 COMMIT
命令成功完成之前,所有的写操作都具有临时性。
这种行为确保了业务逻辑的完整性。例如,在将金额从一个账户转移到另一个账户时,数据库的两行记录需要被修改:一行减少金额,另一行增加金额。如果在这两次写操作之间发生磁盘故障或断电,事务的原子性属性将保证,要么两次修改都不被写入数据库(如果失败或执行了 ROLLBACK
),要么两次修改都写入数据库(如果成功 COMMIT
)。
更详细的信息可以参考 Wikipedia 原子性。
一致性 (Consistency)
事务保证数据库在事务结束后处于一致状态。这种一致性体现在多个层面:
- 数据和所有派生的索引条目是同步的。尽管数据和索引可能存储在不同区域,但在事务结束后,它们都必须被更新或都不被更新。
- 表约束和列约束在事务中可能被违反(通过
DEFERRABLE
关键字允许延迟验证),但在事务结束后必须满足。 - 主键和外键规则可能在事务中被违反(使用
DEFERRABLE
),但事务结束后必须恢复一致性。 - 事务无法自动保证逻辑完整性。例如,在银行转账示例中,如果应用程序忘记更新目标账户,数据库的一致性仍可能受损。
隔离性 (Isolation)
在大多数情况下,多会话同时访问 DBMS。只要数据未被修改,多个会话同时读取数据并不会造成问题。然而,当多个会话尝试同时修改数据时,不可避免地会产生冲突。
示例:
两个会话(S1 和 S2)同时操作航班预订系统:
- S1 读取某航班的剩余座位数:1。
- S2 读取同一航班的剩余座位数:1。
- S1 预订最后一个座位。
- S2 也预订最后一个座位。
上述冲突可以通过让相关事务按顺序执行来避免。然而,这种行为会降低效率。因此,SQL 标准提供了一种机制,通过 SET TRANSACTION
命令定义事务的隔离级别,尽可能允许并行操作。
隔离问题分类:
- 脏读 (Dirty read):事务 T1 修改了一行数据,事务 T2 在 T1 提交之前读取了该行。如果 T1 回滚,则 T2 读取的数据实际上从未存在。
- 不可重复读 (Non-repeatable read):事务 T1 读取了一行数据,事务 T2 随后修改或删除了该行并提交。T1 再次读取该行时,可能会发现数据已更改或不存在。
- 幻读 (Phantom):事务 T1 读取满足条件的多行数据,事务 T2 插入或删除了一些满足条件的新数据。T1 再次读取时,发现结果集发生变化。
隔离级别与问题的对应关系:
通过 SET TRANSACTION
命令,可以定义事务允许或不允许的隔离问题。示例如下:
SET TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE];
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted | 可能 | 可能 | 可能 |
Read Committed | 不可能 | 可能 | 可能 |
Repeatable Read | 不可能 | 不可能 | 可能 |
Serializable | 不可能 | 不可能 | 不可能 |
更多信息请参考 Wikipedia 隔离级别。
持久性 (Durability)
事务保证所有已提交的写操作即使在系统崩溃后也能被恢复。为此,大多数 DBMS 会将数据修改记录在数据库和日志文件中(通常存储在不同设备上)。即使发生磁盘崩溃,数据库也能通过备份和日志文件恢复所有已提交的更改。
更详细的信息请参考 Wikipedia 持久性。
自动提交 (Autocommit)
一些 DBMS(如 MySQL)提供了 自动提交 (AUTOCOMMIT) 功能。启用该功能后,每次写操作后系统会自动执行 COMMIT
。这意味着无法回滚由多个 SQL 语句组成的逻辑单元,也无法使用保存点(SAVEPOINT
)功能。
大多数情况下,自动提交功能默认是开启的。