SQL 更多知识:事务处理

在数据库操作中,事务处理是保障数据一致性和完整性的核心机制。无论是电商平台的订单支付、银行系统的转账交易,还是企业的库存管理,都需要通过事务确保一系列 SQL 操作要么全部成功执行,要么全部失败回滚,避免出现数据错乱的情况。

本章将介绍事务处理的概念、控制事务处理的方法,以及回滚(rollback)、提交(commit)、保留点(savepoint)相关知识。

 

事务简介

事务(Transaction)是由一组 SQL 语句组成的逻辑执行单元,这组语句要么全部执行成功,要么全部执行失败,不存在 “部分成功” 的中间状态。

事务具有四个核心特性,通常被称为 ACID 特性,特性如下:

  • 原子性(Atomicity):事务是一个不可分割的整体,所有操作要么全部完成,要么全部回滚,不会中断在中间环节。

  • 一致性(Consistency):事务执行前后,数据库的完整性约束不会被破坏。例如转账业务中,转出账户减少的金额与转入账户增加的金额必须相等。

  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行结果不会被其他事务干扰,每个事务都感觉不到其他事务的存在。

  • 持久性(Durability):事务一旦提交(commit),其执行结果将永久保存到数据库中,即使后续数据库发生故障,数据也不会丢失。

在 SQL 中,并非所有数据库引擎都支持事务。例如 MySQL 中的 MyISAM 引擎不支持事务,而 InnoDB 引擎是支持事务的;Oracle、SQL Server 等主流数据库则默认支持事务。因此,在使用事务前,需确保所使用的数据库引擎支持事务。

 

控制事务处理

控制事务处理的核心目的是管理事务的开始、执行、提交和回滚,确保事务符合 ACID 特性。不同数据库对事务控制的语法略有差异,但核心逻辑一致。通常,事务的控制包括以下关键操作:

  • 启动事务:标记事务的开始边界,后续的 SQL 语句将被纳入该事务的执行单元。

  • 执行事务内的SQL语句:如插入(INSERT)、更新(UPDATE)、删除(DELETE)等数据操作语句。

  • 提交事务:确认事务内所有 SQL 语句执行成功,将结果永久保存到数据库。

  • 回滚事务:若事务内某条 SQL 语句执行失败,撤销事务内所有已执行的操作,恢复到事务开始前的状态。

  • 设置保留点:在事务内设置中间标记,允许回滚到指定标记位置,而无需回滚整个事务。

注意:SQL 中的查询语句(SELECT)不会修改数据,因此即使在事务内执行,也不会影响事务的一致性,回滚时无需撤销查询操作。

 

回滚事务 ROLLBACK

ROLLBACK 语句用于撤销事务内所有已执行的未提交操作,将数据库状态恢复到事务开始前的状态。它通常在以下场景使用:

  • 事务内某条 SQL 语句执行失败(如违反约束、数据不存在等)。

  • 业务逻辑需要终止事务(如转账过程中发现账户余额不足)。

ROLLBACK 的语法格式如下:

ROLLBACK; -- 回滚整个事务
ROLLBACK TO 保留点名称; -- 回滚到指定保留点(后续讲解保留点时详细说明)

示例:以 MySQL(InnoDB 引擎)为例,假设我们有一个user_account表,存储用户的账户信息,结构如下:

CREATE TABLE user_account (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    balance DECIMAL(10,2) NOT NULL DEFAULT 0.00
);

插入测试数据:

INSERT INTO user_account (username, balance) VALUES ('张三', 1000.00), ('李四', 500.00);
-- 查询数据结果
> select * from user_account
id|username|balance|
--+--------+-------+
 1|张三      |1000.00|
 2|李四      | 500.00|
2 row(s) fetched.

现在模拟转账场景:张三向李四转账300元,但过程中出现错误(如手动触发回滚):

-- 启动事务(MySQL中默认自动提交,需先关闭自动提交或显式启动事务)
> START TRANSACTION
0 row(s) modified.
-- 1. 张三账户减少300元
> UPDATE user_account SET balance = balance - 300 WHERE username = '张三'
1 row(s) modified.
-- 2. 模拟错误:手动触发回滚(实际场景中可能是其他语句执行失败)
> ROLLBACK
0 row(s) modified.
-- 验证结果:查询账户余额,发现张三和李四的余额均未变化
> SELECT * FROM user_account
id|username|balance|
--+--------+-------+
 1|张三      |1000.00|
 2|李四      | 500.00|
2 row(s) fetched.

执行结果:张三的余额仍为 1000.00 元,李四的余额仍为 500.00 元,说明 ROLLBACK 成功撤销了 UPDATE 操作。

  

提交事务 COMMIT

COMMIT 语句用于确认事务内所有 SQL 语句执行成功,将操作结果永久保存到数据库中。提交后,事务结束,且无法再通过 ROLLBACK 撤销已提交的操作(除非借助数据库备份恢复)。它是事务完成的标志,通常在以下场景使用:

  • 事务内所有 SQL 语句均执行成功,符合业务逻辑要求。

  • 业务流程完成(如转账成功、订单创建完成)。

语法格式如下:

COMMIT;

示例:继续使用上述user_account表,模拟正常的转账流程并提交事务:

-- 启动事务
> START TRANSACTION
0 row(s) modified.
-- 1. 张三账户减少300元
> UPDATE user_account SET balance = balance - 300 WHERE username = '张三'
1 row(s) modified.
-- 2. 李四账户增加300元
> UPDATE user_account SET balance = balance + 300 WHERE username = '李四'
1 row(s) modified.
-- 3. 提交事务(确认所有操作成功)
> COMMIT
0 row(s) modified.
-- 验证结果:查询账户余额
> SELECT * FROM user_account
id|username|balance|
--+--------+-------+
 1|张三      | 700.00|
 2|李四      | 800.00|
2 row(s) fetched.

执行结果:张三的余额变为 700.00 元,李四的余额变为 800.00 元,说明事务提交成功,操作结果永久保存。此时再执行 ROLLBACK 已无法撤销操作。

注意:不同数据库的默认提交模式不同。例如 MySQL 默认自动提交(每条 SQL 语句单独作为一个事务自动提交),因此在使用显式事务时,需通过START TRANSACTION启动事务,或通过SET AUTOCOMMIT = 0关闭自动提交;Oracle 默认关闭自动提交,需显式使用 COMMIT 提交事务。

  

保留点 SAVEPOINT

保留点是事务内设置的中间标记,用于将事务分割为多个较小的执行单元。当需要回滚时,可通过 ROLLBACK TO 语句回滚到指定保留点,而无需回滚整个事务。这在复杂事务中非常实用,例如一个事务包含多个独立的业务操作,其中某个操作失败时,只需回滚该操作相关的步骤,而保留其他已成功的操作。

语法格式如下:

-- 设置保留点
SAVEPOINT 保留点名称;
-- 回滚到指定保留点
ROLLBACK TO 保留点名称;
-- 删除保留点(可选,事务提交或回滚后保留点自动失效)
RELEASE SAVEPOINT 保留点名称;

示例:假设我们有一个更复杂的业务场景:张三向李四转账 300 元,同时张三还需要向王五转账 200 元(王五账户不存在),此时我们希望只回滚向王五转账的操作,保留向李四转账的操作。

首先插入王五的账户(若不插入,后续 UPDATE 会失败):

INSERT INTO user_account (username, balance) VALUES ('王五', 800.00);

执行复杂事务并设置保留点:

-- 启动事务
> START TRANSACTION
0 row(s) modified.
-- 1. 张三向李四转账300元
> UPDATE user_account SET balance = balance - 300 WHERE username = '张三'
1 row(s) modified.
> UPDATE user_account SET balance = balance + 300 WHERE username = '李四'
1 row(s) modified.
-- 设置保留点:after_transfer_lisi(转账李四后)
> SAVEPOINT after_transfer_lisi
0 row(s) modified.
-- 2. 张三向王五转账200元(假设此处执行失败,如手动触发回滚)
> UPDATE user_account SET balance = balance - 200 WHERE username = '张三'
1 row(s) modified.
> UPDATE user_account SET balance = balance + 200 WHERE username = '王五'
0 row(s) modified.
-- 模拟错误:回滚到after_transfer_lisi保留点,撤销向王五转账的操作
> ROLLBACK TO after_transfer_lisi
0 row(s) modified.
-- 提交事务:保留向李四转账的操作
> COMMIT
0 row(s) modified.
-- 验证结果
> SELECT * FROM user_account
id|username|balance|
--+--------+-------+
 1|张三      | 400.00|
 2|李四      |1100.00|
2 row(s) fetched.

执行结果:

  • 张三的余额:700.00 - 300.00 = 400.00(向王五转账的 200 元被回滚,未扣除)。

  • 李四的余额:800.00 + 300.00 = 1100.00(操作保留)。

  • 王五的余额:800.00(未增加 200 元,操作被回滚)。

通过保留点,我们成功实现了 “部分回滚”,避免了因某个局部操作失败而导致整个事务被撤销,提高了事务处理的灵活性。

 

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号