一,定义
- 事务是一组操作的集合,它是一个不可分割的工作单位。
- 事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,这些操作要么同时成功,要么同时失败
二,流程
开启事务---->提交事务
如果中间出现了异常,就回滚事务,回滚事务指的是将修改的数据恢复。
注意:MYSQL中默认事务自动提交,即执行DML语句的时候MYSQL会立即隐式的提交事务.
三,事务的操作演示
以转账为例
假定有这样一个账单
| 账户 | 余额 |
|---|---|
| 张三 | 2000 |
| 李四 | 2000 |
现进行以下操作
-
查询张三的余额
select * from account where name='张三'; -
对张三的余额-1000
update account set money=money-1000 where name='张三'; -
对李四的余额+1000
update account set money=money+1000 where name='李四';
对这三条指令在同一时间执行那么表会变成
| 账户 | 余额 |
|---|---|
| 张三 | 1000 |
| 李四 | 3000 |
假定2--3中存在SQL语句A,且A中SQL语句必然不能执行成功
这张表就会变成
| 账户 | 余额 |
|---|---|
| 张三 | 1000 |
| 李四 | 2000 |
那么将1,2,A,3的代码在同一时间执行,就会出现1,2执行成功A,3执行不超过
这时候我们回滚事务即可。
在MYSQL中每一条DML语句都是一个单独的事务,并且默认自动提交。
3.1 事务的执行语法
查看事务提交方式
Select @@autocommit;
如果是1代表手动提交,0代码自动提交。在MYSQL中@@代表系统变量,@代表用户自定义变量。
设置事务的提交方式
Set @@autocommit=0或者1;(这个是将整个控制台提交方式的修改)
或者
begin; 开启事务
Start transcation; 开启事务
提交事务
commit;
回滚事务
rollback;
一般事务用法:
begin或start transcation
SQL语句
commit;
出现错误
回滚事务
roolback;
四,事务的四大特性(ACID)
4.1 原子型(Atomicity)
- 定义:事务的所有操作要么全部成功提交,要么全部失败回滚,不存在中间状态。
- 实现机制:
- Undo Log(回滚日志):记录事务修改前的数据快照。若事务回滚,InnoDB利用Undo Log逆向恢复数据。
- 事务状态管理:事务的提交(COMMIT)或回滚(ROLLBACK)操作由引擎内部原子性保证。
- 示例:转账事务中,若扣款成功但加款失败,系统自动回滚,确保两个账户余额不变。
4.2 一致性(Consistency)
- 定义:事务执行前后,数据库必须满足所有预定义的业务规则和约束(如唯一索引、外键、数据类型等)。
- 实现机制:
- 数据库约束:如主键、外键、NOT NULL、CHECK约束(MySQL 8.0+强化支持)。
- 应用层逻辑:开发者需确保业务逻辑的正确性(例如转账前后总额不变)。
- 原子性、隔离性、持久性的共同保障:其他三个ACID特性最终服务于一致性。
- 示例:若用户定义“余额不可为负”,事务中的扣款操作会因违反约束而失败,保持数据一致性。
4.3 隔离性(Isolation)
-
定义:并发事务的执行互不干扰,避免数据不一致。
-
隔离级别与问题:
隔离级别 脏读 不可重复读 幻读 READ UNCOMMITTED ✔️ ✔️ ✔️ READ COMMITTED ✖️ ✔️ ✔️ REPEATABLE READ (默认) ✖️ ✖️ ✖️* SERIALIZABLE ✖️ ✖️ ✖️ *注:InnoDB的REPEATABLE READ通过Next-Key锁避免幻读。
-
实现机制:
- MVCC(多版本并发控制):
- Read View:事务启动时生成数据快照,确保读取一致性。
- Undo Log链:提供历史版本数据,支持非锁定读。
- 锁机制:
- 行锁:锁定被修改的行,防止其他事务写冲突。
- 间隙锁(Gap Locks)与Next-Key锁:锁定索引范围,防止幻读。
- MVCC(多版本并发控制):
-
示例:在REPEATABLE READ级别下,事务A多次读取同一数据结果一致,即使事务B已修改并提交。
4.4 持久性(Durability)
- 定义:事务提交后,数据永久保存,即使系统崩溃也不丢失。
- 实现机制:
- Redo Log(重做日志):
- 记录物理修改(如页的变更),事务提交时先写入Redo Log,再异步刷盘。
- 崩溃恢复时,重放Redo Log恢复未写入数据文件的操作。
- Double Write Buffer:
- 防止页断裂(Partial Page Write),确保数据页写入的完整性。
- Binlog(归档日志):
- 用于主从复制,通过两阶段提交与Redo Log协作保证一致性。
- Redo Log(重做日志):
- 示例:事务提交后,即使数据库立即崩溃,重启后仍能通过Redo Log恢复数据。
五,并发事务产生的问题
A事务和B事务同时操作同一张表引发的问题叫并发事务问题,就多个操作端,同时操作一张表产生的问题。
测试:
-- 创建一个银行账户表
CREATE TABLE bank_account (
id INT PRIMARY KEY AUTO_INCREMENT,
account_name VARCHAR(50) NOT NULL,
balance DECIMAL(10, 2) NOT NULL DEFAULT 0.00
) ENGINE=InnoDB; -- 必须使用 InnoDB 引擎(支持事务)
-- 插入初始数据
INSERT INTO bank_account (account_name, balance)
VALUES
('Alice', 1000.00),
('Bob', 500.00);
5.1 脏读
事务A读取到了事务B未提交的数据
-
设置隔离级别为 READ UNCOMMITTED:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -
在会话 A 中执行未提交的写操作:
-- 会话 A BEGIN; UPDATE bank_account SET balance = balance - 100 WHERE account_name = 'Alice'; -- 注意:不提交事务! -
在会话 B 中读取未提交的数据:
-- 会话 B SELECT * FROM bank_account WHERE account_name = 'Alice';结果:Alice 的余额变为
900.00(即使会话 A 未提交)。
总结:
- 事务A更新数据
- 事务B查询数据,查询到了事务A未提交的数据
5.2 不可重复读
在一个事务中,查询俩次前后查询数据不一致的叫不可重复读
-
设置隔离级别为 READ COMMITTED:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -
在会话 A 中多次查询同一数据:
-- 会话 A BEGIN; SELECT balance FROM bank_account WHERE account_name = 'Alice'; -- 第一次查询 -- 结果:1000.00 -
在会话 B 中修改并提交数据:
-- 会话 B BEGIN; UPDATE bank_account SET balance = balance - 100 WHERE account_name = 'Alice'; COMMIT; -
在会话 A 中再次查询:
SELECT balance FROM bank_account WHERE account_name = 'Alice'; -- 第二次查询 -- 结果:900.00(两次结果不一致)
总结:
- 事务A执行1查询
- 事务B更新
- 事务A执行2查询
5.3 幻读
在一个事务内,多次查询同一范围的数据,后一次查询发现了前一次查询未出现的新行(这些新行是其他事务插入并提交的)。
-
设置隔离级别为 REPEATABLE READ:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; -
在会话 A 中查询范围数据:
-- 会话 A BEGIN; SELECT * FROM bank_account WHERE balance > 800; -- 结果:Alice(1000.00) -
在会话 B 中插入新数据并提交:
-- 会话 B BEGIN; INSERT INTO bank_account (account_name, balance) VALUES ('Charlie', 900.00); COMMIT; -
在会话 A 中再次查询:
SELECT * FROM bank_account WHERE balance > 800; -- 结果:仍只有 Alice(REPEATABLE READ 通过 MVCC 避免幻读)
六,并发事务的隔离级别
-
√代表会出现
-
×代表不会出现
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| Read uncommitted | √ | √ | √ |
| Read committed | × | √ | √ |
| Repeatable Read(默认) | × | × | × |
| Serializable | × | × | × |
从read uncommitted到serializable 隔离级别依次提高,性能依次下降。
语法
查询当前事务的隔离级别
select @@transaction_isolation;
设置事务的隔离级别
set [session/global] transaction isolation level [级别];
session代表对当前窗口有效,global代表对全部窗口有效