事务(Transaction)是数据库管理系统中一组操作的集合,作为一个单元要么全部成功,要么全部失败,确保数据的一致性和完整性。它像一个"原子操作单元",遵循ACID原则(原子性、一致性、隔离性、持久性)。
特性
-
原子性(Atomicity)
- 事务是最小工作单元,不可再分。事务中的操作要么全部执行成功,要么全部不执行(回滚)。
- 确保操作的"不可分割"。
-
一致性(Consistency)
- 数据库总是从一个一致的状态转换成另一个一致状态,不会因为某条语句失败而出现其他状态。事务完成后,数据库必须保持一致的状态(符合所有约束、规则)。
- 不会产生不合法的数据。
-
隔离性(Isolation)
-
多个事务并发执行时,彼此之间的操作互不干扰,表现为独立状态。通常来说,一个事务所做的修改在最终提交前,对其他事务是不可见的。
-
通过锁机制实现。
-
-
持久性(Durability)
- 事务一旦提交,其结果对数据库是永久的,即使系统崩溃也不会丢失。
一个兼容 ACID 的数据库系统很多复杂但可能用户并没有觉察到的工作。 相比没有实现 ACID 的数据库,通常会需要更强的 CPU 处理能力、更大的内存和更多的磁盘空间。
事务的基本操作(SQL命令)
-
begin /start transaction
开始一个新事务。
-
commit
提交事务,应用所有操作的变化,结束事务。
-
rollback
回滚事务,将操作状态恢复到开始前的状态。
-
savepoint / release savepoint / rollback to savepoint
保存点:在事务中设定"中间点",可以回滚到某个保存点。
流程示意
sql
begin; -- 开始事务
-- 执行一系列操作
update account set balance = balance - 100 where id=1;
update account set balance = balance + 100 where id=2;
-- 根据条件决定
if success then
commit; -- 提交事务
else
rollback; -- 回滚事务
end if;
使用事务
- 开始标志:任何一条 DML 语句的执行
- 结束标志:
- 提交:成功的结束,将所有的DML语句操作记录和底层硬盘文件中数据进行同步
- 回滚:失败的结束:将所有 DML 语句操作记录全部清除
MySQL 默认是自动提交
sql
#开启事务
start transaction;
#结束事务
end transaction;
#提交事务
commit;
#保存回滚点
savepoint 回滚点名;
#回滚事务
rollback[ to 回滚点名];
#查看事务的提交方式
show variables like 'autocommit';
#启用和关闭自动提交模式
set autocommited=1;
set autocommited=0;
例:转账业务
sql
drop table if exists account;
create table account
(
id int primary key auto_increment,
name varchar(20),
money int
);
insert into account
(name, money)
values
("lz", 20000),
("xz", 500);
- 自动提交和手动提交,再开一个 MySQL-Front 窗口查看数据变化
- MySQL默认会在每个SQL语句执行完后,自动提交事务(数据变更立即生效)。
- 关闭自动提交:通过调整设置,可以让事务变成"手动提交",即你写入操作后,需要明确调用commit来提交,否则更改不会生效。
sql
#执行语句,再打开一个 MySQL-Front窗口,查询是否有变化
update account set money=money-1000 where name="lz";
update account set money=money+1000 where name="xz";
select * from account;
#查看提交方式,改为手动提交
# 查看当前autocommit(自动提交)设置的状态(值是ON或OFF)。
show variables like 'autocommit';
set autocommit=off;
#手动提交
commit;
隔离级别(控制事务间的干扰)
-
读未提交(Read Uncommitted):事务可以读取未提交的变更,可能会出现脏读。这个级别会导致很多问题,性能也没有好太多,一般很少使用。
-
读已提交(Read Committed):只能读取已提交的数据,防止脏读,但可能出现不可重复读。
-
可重复读(Repeatable Read) :在事务期间多次读取相同数据结果一致,避免不可重复读。该级别无法解决幻读问题。该级别是 MySQL 的默认隔离级别,并且 MySQL 在该级别就可以很大程度解决幻读问题。
-
串行化(Serializable):强制事务串行执行,避免幻读问题。该级别会在读取的每一行数据上都加锁,可能导致大量的超时和锁争用的问题。实际很少用这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

- 查看隔离级别:
sql
#查看会话级的当前隔离级别:(会话级只对当前窗口有效)
select @@tx_isolation;
select @@session.tx_isolation;
#查看全局级的当前隔离级别:
select @@global.tx_isolation;
- 设置隔离级别 :
- 方法1:my.ini 文件中的 [mysqld] 下面添加transaction-isolation=隔离级别
- 方法2:通过命令
sql
#设置全局或当前会话范围
set global transaction isolation level 隔离级别;# 全局
set session transaction isolation level 隔离级别;# 当前
- 不同隔离级别下a1、a2、a3的值为多少?
- 未提交读:2、2、2
- 提交读:1、2、2
- 可重复读:1、1、2(可能出现幻读)
- 串行化:1、1、2

- 实现隔离级别的两种方法:
- 加锁:读数据前对其加锁,阻止其他事务对数据修改
- 快照:读取数据的快照(之前的版本)实现,例如,可重复读级别在整个事务期间都读取事务开始时的快照去解决不可重复读问题。
并行事务带来的问题
并行事务指的是在数据库中同时执行的多个事务,它们在时间上交叠进行,但相互之间是并发处理的。
- 数据一致性问题:
- 脏读(dirty read):一个事务读到另一个事务未提交的数据就叫做脏读。一个事务对一条记录做修改,在这个事务提交前是有可能随时回滚的,这些数据就是脏数据。
- 不可重复读(non-repeatable read):在一个事务内多次读取同一条记录,前后结果不一样就叫做不可重复读。事务 A 多次查询同一行数据,在多次查询中间,事务 B 对该行数据进行了修改,事务 A 多次查询同一行的数据就会不一致。
- 幻读(phantom read):在一个事务内按相同条件多次查询,前后结果集的数量不同就叫做幻读。事务 A 按 id>5 的条件进行多次查询,在多次查询中间,事务 B 插入了一条 id 为6的数据,事务 A 多次查询的结果集数量就会不同。
- 影响:
- 脏读:读到其他事务未提交的数据
- 不可重复读:前后读取数据不一致
- 幻读:前后读取的结果集数量不同