一、什么是 MySQL 事务?

逻辑操作单元中的事务是一组不可分割的工作逻辑单元,其作用是实现数据状态之间的转换。以转账为例,用户A向用户B转账50元时,需同时从用户A账户扣除50元并向用户B账户增加50元。事务将多个操作命令作为一个完整集合提交至系统,确保这些命令要么全部成功执行,要么在失败时全部撤销,从而保持数据的一致性与操作的原子性。

二、MySQL 事务的四大核心特性(ACID)

原子性(Atomicity)

原子性要求事务是不可分割的最小工作单元,事务内的所有操作要么全部执行成功并提交,要么全部执行失败并回滚,不存在部分执行的中间状态。

一致性(Consistency)

一致性指事务执行前后,数据库的数据始终从一个合法状态转换为另一个合法状态。该合法性基于业务语义而非语法规则,由业务预定的各类约束(如数据校验规则、关联关系约束、业务逻辑规则等)定义,满足所有约束的数据状态即为合法状态。

隔离性(Isolation)

隔离性保障并发执行的多个事务之间相互独立、互不干扰。单个事务内部的操作及所使用的数据,对并发的其他事务保持隔离状态,任一事务的执行过程不会被其他并发事务打断或篡改。

持久性(Durability)

持久性意味着事务一旦提交成功,其对数据库数据做出的修改将成为永久性变更。后续的任何数据库操作、系统故障或服务重启,都不会改变该事务已提交的结果,修改的数据会被持久化存储并保证可访问。

三、事物的状态

活跃的(Active)

当事务对应的数据库操作正在执行过程中时,该事务即处于活动的状态。

部分提交的(Partially Committed)

当事务中的最后一个操作执行完成,但操作产生的结果仅暂存于内存,尚未刷新至磁盘持久化时,该事务即处于部分提交的状态。

失败的(Failed)

若事务处于活动的部分提交的状态时,因数据库自身错误、操作系统故障、突发断电等原因无法继续执行,或被人为主动终止执行,该事务即进入失败的状态。

终止的(Aborted)

当事务执行中途进入失败的状态后,需将已执行操作对数据库做出的修改还原至事务执行前的初始状态,这个撤销修改的过程即为回滚。当回滚操作全部完成,数据库恢复至事务执行前的状态时,该事务即处于终止的状态。

四、由事物引发的并发读写问题

首先关闭自动提交,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(串行化):可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免,但性能十分低下。能避免脏读、不可重复读和幻读。

隔离级别总结

六、总结

事物的定义

ACID特性

  • 原子性:最小工作单元,操作要么全提交成功,要么全失败回滚

  • 一致性:事务执行前后数据均满足业务约束(语义层面),始终处于合法状态

  • 隔离性:并发事务互不干扰,单个事务的操作 / 数据对其他事务保持隔离

  • 持久性:事务提交后,数据修改永久生效,不受后续操作、系统故障等影响

事务的生命周期状态

活跃的:事务对应的数据库操作正在执行中;

部分提交的:事务最后一个操作完成,但结果仅暂存内存,未持久化到磁盘;

失败的:事务在活跃 / 部分提交状态下因故障或人为终止,无法继续执行;

终止的:失败事务完成回滚,数据库恢复至事务执行前的初始状态。

事务并发引发的读写问题

  • 脏写:未提交的事务覆盖其他已提交事务的修改,导致数据被错误覆盖

  • 脏读:读取到其他事务未提交的临时数据,若该事务回滚,则读取的数据无效

  • 不可重复读:同一事务内多次读取同一数据,因其他事务提交修改,导致读取结果不一致

  • 幻读:同一事务内多次读取全表 / 指定范围数据,因其他事务插入新数据,导致结果行数变化

隔离级别

  • 读未提交(RU) :最低级别,允许读取未提交数据,无法解决任何并发问题;

  • 读已提交(RC) :可避免脏读,但仍存在不可重复读、幻读(多数数据库默认级别);

  • 可重复读(RR) :MySQL 默认级别,可避免脏读、不可重复读,仍存在幻读;

  • 串行化(SERIALIZABLE) :最高级别,完全避免所有并发问题,但性能极低(串行执行事务)。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com