星云点击:星空遥控器
120.47M · 2026-02-04
逻辑操作单元中的事务是一组不可分割的工作逻辑单元,其作用是实现数据状态之间的转换。以转账为例,用户A向用户B转账50元时,需同时从用户A账户扣除50元并向用户B账户增加50元。事务将多个操作命令作为一个完整集合提交至系统,确保这些命令要么全部成功执行,要么在失败时全部撤销,从而保持数据的一致性与操作的原子性。
原子性要求事务是不可分割的最小工作单元,事务内的所有操作要么全部执行成功并提交,要么全部执行失败并回滚,不存在部分执行的中间状态。
一致性指事务执行前后,数据库的数据始终从一个合法状态转换为另一个合法状态。该合法性基于业务语义而非语法规则,由业务预定的各类约束(如数据校验规则、关联关系约束、业务逻辑规则等)定义,满足所有约束的数据状态即为合法状态。
隔离性保障并发执行的多个事务之间相互独立、互不干扰。单个事务内部的操作及所使用的数据,对并发的其他事务保持隔离状态,任一事务的执行过程不会被其他并发事务打断或篡改。
持久性意味着事务一旦提交成功,其对数据库数据做出的修改将成为永久性变更。后续的任何数据库操作、系统故障或服务重启,都不会改变该事务已提交的结果,修改的数据会被持久化存储并保证可访问。
当事务对应的数据库操作正在执行过程中时,该事务即处于活动的状态。
当事务中的最后一个操作执行完成,但操作产生的结果仅暂存于内存,尚未刷新至磁盘持久化时,该事务即处于部分提交的状态。
若事务处于活动的或部分提交的状态时,因数据库自身错误、操作系统故障、突发断电等原因无法继续执行,或被人为主动终止执行,该事务即进入失败的状态。
当事务执行中途进入失败的状态后,需将已执行操作对数据库做出的修改还原至事务执行前的初始状态,这个撤销修改的过程即为回滚。当回滚操作全部完成,数据库恢复至事务执行前的状态时,该事务即处于终止的状态。
首先关闭自动提交,mysql 默认是自动提交事物,所以这个需要提前关闭;
-- 查看事物的提交方式 1 自动提交 0 手动提交
select @@AUTOCOMMIT
-- 设置手动提交
set AUTOCOMMIT = 0
接下来会用MySQL5.7版本来演示,演示数据
CREATE TABLE `tb_account` (
`account` int(11) NULL DEFAULT NULL COMMENT '账号',
`money` double NULL DEFAULT NULL COMMENT '金额'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of tb_account
-- ----------------------------
INSERT INTO `tb_account` VALUES (10087, 2000);
INSERT INTO `tb_account` VALUES (10086, 2000);
有两个用户A,B。并发的去修改account为10087这条数据的账户余额字段,原始账户余额是2000,此时A事物开启将2000修改成2200,此时没有还未提交事物,此时开启B事物将余额修改成2300,提交并持久化再数据库中,然后再提交A事物,此时B事物修改的值会被A事物覆盖掉,此时就产生脏写问题。
假如现在还是有两个用户A,B,并发的去修改account为10087这条数据的账户余额字段,原始账户余额是2000,B事物开启,修改这个账户的余额未2300,此时事物A读取到的数据就是money = 2300,此时事物B回滚,那么事物A就相当于读到了一个不存在的数据。
事物B
事物B首先设置隔离级别读未提交,开启事物,更新余额为2300
事物A
事物A同时也需要设置隔离级别为读未提交,开始读取account为10087这条数据的余额,发现是2300
事物B
事物B开始回滚
事物A
事物A再次读取,发现余额字段值变成2300
假如现在还是有两个事物A和B。此时A事物去读取了一个字段,之后B事物更新了这个字段,之后A事物再次去读取同一个字段值就不同了,就意味着发生了不可重复度。
还是用account=10087这条数据,初始值
事物B
事物B先去更新account=10087这条数据的money值为2300,这个时候先不要提交,设置这个隔离级别为读已提交
事物A
事物A同样开启隔离级别为读已提交,记得开启事物,明显可以看到读到的还是之前的数据,因为事物B还未提交,解决了脏读的问题。
事物B
事物B提交事物,执行commit命令
事物A
此时事物A再去读取,这个money的值就变成2300了,产生不可重复读,记得要开启事物
假如现在还是有两个事物A和B。此时A事物去读取所有表中所有的列,之后用户B事物又重新的插入了一条,此时A事物再去读取的时候就会多出来一条数据,就像是产生幻觉一样。
目前表里面的初始值为
事物A
隔离级别读已提交,开启事物,然后读取tb_account表里面内容
事物B
设置事物B的隔离级别为读已提交,开启事物,然后做数据插入,并提交数据
事物A
此时事物A再去读取tb_account表里面内容,发现多了一条数据,类似产生幻觉
设置隔离级别,隔离级别分成4种分别是:
READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE。
READ UNCOMMITTED(读未提交 RU):读未提交,在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。不能避免脏读、不可重复读、幻读。
READ COMMITTED(读已提交 RC):读已提交,它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。可以避免脏读,但不可重复读、幻读问题仍然存在。
REPEATABLE READ (可重复读 RR):可重复读,事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容。可以避免脏读、不可重复读,但幻读问题仍然存在。这是MySQL的默认隔离级别。
SERIALIZABLE(串行化):可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免,但性能十分低下。能避免脏读、不可重复读和幻读。
隔离级别总结
原子性:最小工作单元,操作要么全提交成功,要么全失败回滚
一致性:事务执行前后数据均满足业务约束(语义层面),始终处于合法状态
隔离性:并发事务互不干扰,单个事务的操作 / 数据对其他事务保持隔离
持久性:事务提交后,数据修改永久生效,不受后续操作、系统故障等影响
活跃的:事务对应的数据库操作正在执行中;
部分提交的:事务最后一个操作完成,但结果仅暂存内存,未持久化到磁盘;
失败的:事务在活跃 / 部分提交状态下因故障或人为终止,无法继续执行;
终止的:失败事务完成回滚,数据库恢复至事务执行前的初始状态。
脏写:未提交的事务覆盖其他已提交事务的修改,导致数据被错误覆盖
脏读:读取到其他事务未提交的临时数据,若该事务回滚,则读取的数据无效
不可重复读:同一事务内多次读取同一数据,因其他事务提交修改,导致读取结果不一致
幻读:同一事务内多次读取全表 / 指定范围数据,因其他事务插入新数据,导致结果行数变化
读未提交(RU) :最低级别,允许读取未提交数据,无法解决任何并发问题;
读已提交(RC) :可避免脏读,但仍存在不可重复读、幻读(多数数据库默认级别);
可重复读(RR) :MySQL 默认级别,可避免脏读、不可重复读,仍存在幻读;
串行化(SERIALIZABLE) :最高级别,完全避免所有并发问题,但性能极低(串行执行事务)。