PostgreSQL 事务详解
在 PostgreSQL 中,事务是一组数据库操作的逻辑单元,这些操作要么全部成功执行,要么全部不执行,以确保数据的一致性和完整性。以下从多个方面对 PostgreSQL 事务进行详解:
- 事务的基本概念
- 定义:事务是一个不可分割的工作单元,由一条或多条 SQL 语句组成。在 PostgreSQL 中,事务可以包含数据查询(
SELECT)、数据修改(INSERT、UPDATE、DELETE)、数据定义(CREATE、ALTER、DROP)等操作。 - 特性(ACID)
- 原子性(Atomicity):事务中的所有操作要么全部成功提交(
COMMIT),要么全部回滚(ROLLBACK),不会出现部分执行的情况。例如,在一个涉及多个表的转账操作中,从账户 A 扣除金额和向账户 B 添加金额的操作必须作为一个整体,要么都完成,要么都不执行,以防止资金丢失或错误增加。 - 一致性(Consistency):事务执行前后,数据库的完整性约束(如主键约束、外键约束、唯一约束等)保持不变。例如,在插入一条新记录时,该记录必须满足所有相关的约束条件,否则事务将无法成功提交。
- 隔离性(Isolation):多个并发事务之间相互隔离,一个事务的执行不会影响其他事务。不同的隔离级别决定了事务之间可见性和干扰程度。PostgreSQL 支持多种隔离级别,如读未提交(
READ UNCOMMITTED,PostgreSQL 中没有直接对应,功能类似的是READ COMMITTED下的并发查询可能读到未提交数据的情况)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ)、可串行化(SERIALIZABLE) 。 - 持久性(Durability):一旦事务成功提交,其对数据库的修改将永久保存,即使系统崩溃或发生故障。这通过日志记录(如预写式日志
Write - Ahead Logging,WAL)来实现,确保已提交的事务不会丢失。
- 原子性(Atomicity):事务中的所有操作要么全部成功提交(
- 定义:事务是一个不可分割的工作单元,由一条或多条 SQL 语句组成。在 PostgreSQL 中,事务可以包含数据查询(
- 事务的操作语句
- 开始事务:在 PostgreSQL 中,默认情况下,每个单独的 SQL 语句本身就是一个事务。但如果要将多个 SQL 语句组合成一个事务,可以使用
BEGIN、START TRANSACTION或BEGIN TRANSACTION语句开始一个事务。例如:
- 开始事务:在 PostgreSQL 中,默认情况下,每个单独的 SQL 语句本身就是一个事务。但如果要将多个 SQL 语句组合成一个事务,可以使用
BEGIN;
-- 或
START TRANSACTION;
- 提交事务:使用
COMMIT语句将事务中所有的修改永久保存到数据库中。一旦执行COMMIT,事务中的操作就无法回滚。示例:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
COMMIT;
- 回滚事务:使用
ROLLBACK语句撤销事务中所有未提交的修改,将数据库恢复到事务开始前的状态。例如,在事务执行过程中发现错误或不满足某些条件时,可以回滚事务:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 检查余额是否足够,若不足则回滚
SELECT balance FROM accounts WHERE account_id = 1;
-- 假设余额不足
ROLLBACK;
- 保存点(Savepoints)
- 定义:保存点是事务中的一个标记点,可以在事务内设置多个保存点。通过设置保存点,可以将事务部分回滚到特定的标记位置,而不是回滚整个事务。
- 操作语句:使用
SAVEPOINT语句创建保存点,例如SAVEPOINT my_savepoint;。使用ROLLBACK TO SAVEPOINT语句回滚到指定的保存点,如ROLLBACK TO SAVEPOINT my_savepoint;。使用RELEASE SAVEPOINT语句删除保存点,如RELEASE SAVEPOINT my_savepoint;。例如:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
SAVEPOINT update_account1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- 发现错误,回滚到第一个更新操作之后的保存点
ROLLBACK TO SAVEPOINT update_account1;
RELEASE SAVEPOINT update_account1;
COMMIT;
- 并发事务与隔离级别
- 并发问题:在多用户并发访问数据库时,如果事务隔离级别设置不当,可能会出现脏读(Dirty Read)、不可重复读(Non - Repeatable Read)、幻读(Phantom Read)等问题。
- 脏读:一个事务读取到另一个未提交事务修改的数据。例如,事务 A 修改了某条记录但未提交,事务 B 此时读取到了该未提交的修改,若事务 A 随后回滚,事务 B 读取的数据就是无效的。
- 不可重复读:在一个事务内多次读取同一数据,由于其他事务的修改,导致每次读取的结果不一致。比如,事务 A 在两次读取某条记录之间,事务 B 修改并提交了该记录,事务 A 再次读取时得到不同的结果。
- 幻读:一个事务按照某个条件查询数据时,两次查询之间,其他事务插入或删除了符合该条件的记录,导致事务 A 两次查询的结果集不同,好像出现了 “幻影” 数据。
- 隔离级别解决并发问题:
- 读已提交(
READ COMMITTED):这是 PostgreSQL 的默认隔离级别。一个事务只能读取到其他已提交事务修改的数据,避免了脏读问题,但仍可能出现不可重复读和幻读。 - 可重复读(
REPEATABLE READ):在一个事务内多次读取同一数据时,保证读取结果一致,解决了不可重复读问题,但可能存在幻读。它通过使用多版本并发控制(MVCC,Multi - Version Concurrency Control)机制,在事务开始时创建一个数据快照,后续读取操作都基于这个快照。 - 可串行化(
SERIALIZABLE):这是最高的隔离级别,完全避免了脏读、不可重复读和幻读问题。它通过对并发事务进行排序,使其像串行执行一样,保证数据的一致性和完整性,但可能会降低系统并发性能。
- 读已提交(
- 并发问题:在多用户并发访问数据库时,如果事务隔离级别设置不当,可能会出现脏读(Dirty Read)、不可重复读(Non - Repeatable Read)、幻读(Phantom Read)等问题。
- 异常处理与自动回滚
- 异常处理:在事务执行过程中,如果发生错误(如违反约束、语法错误等),PostgreSQL 会根据错误的严重程度和事务的状态进行处理。对于一些严重错误,事务会自动回滚;对于一些可恢复的错误,可以通过异常处理机制(如
EXCEPTION块,在 PL/pgSQL 中使用)来捕获和处理错误,决定是否回滚事务。 - 自动回滚:如果在事务中执行的 SQL 语句违反了数据库的完整性约束(如插入重复的主键值)、发生了内部错误(如内存不足)或超出了资源限制,PostgreSQL 会自动回滚事务,确保数据库的一致性。例如:
- 异常处理:在事务执行过程中,如果发生错误(如违反约束、语法错误等),PostgreSQL 会根据错误的严重程度和事务的状态进行处理。对于一些严重错误,事务会自动回滚;对于一些可恢复的错误,可以通过异常处理机制(如
BEGIN;
INSERT INTO unique_table (unique_column) VALUES ('duplicate_value'); -- unique_column有唯一约束
-- 由于违反唯一约束,事务会自动回滚
浙公网安备 33010602011771号