MySQL 进阶必学:事务管理(ACID + 隔离级别 + 手动提交 + MVCC)—-《Hello MySQL!》(7)
文章目录
- 前言
- 事务的概念
- 事务的版本支持
- 事务的提交方式
-
- 手动提交
- 事务的隔离级别
-
- 查看和设置隔离级别
- 更深层次的理解隔离性
-
- 理解MVCC
- 作业部分
前言
事务是 MySQL 保证数据一致性的核心机制,也是后端开发、面试的高频考点。但实际开发中,很多人只会简单使用事务,却不懂 ACID 的真正含义、分不清隔离级别的适用场景、不理解 MVCC 的工作原理,导致事务提交踩坑、数据错乱、并发异常。
本文不讲冗余理论,只聚焦实战与核心:涵盖 MySQL 事务的概念、ACID 四大属性,详解事务的版本支持、提交方式(自动 / 手动)、保存点与回滚操作,拆解四种隔离级别的差异及对应的并发问题(脏读 / 幻读等),深入讲解 MVCC 多版本并发控制的核心逻辑,附带经典作业解析,帮你快速掌握事务管理的核心用法,避开常见误区,确保并发场景下数据一致。
事务的概念
事务就是一组DML语句组成,这些语句在逻辑上存在相关性,这一组DML语句要么全部成功,要么全部失败,是一个整体
事务需要具有4个属性:简称
ACID原子性(不可分割性)(automicity)
一致性(consistency):事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态(技术上是通过
AID保证的C)隔离性(又称独立性)(isolation)
持久性(durability):事务处理结束后,对数据的修改是永久的,即使系统故障也不会丢失
事务的版本支持
查看方法:
show engines G
注意:不是所有的引擎都支持事务的
引申:
G是大小写敏感的!
事务的提交方式
查看事务的提交方式:
show variables like 'autocommit';
on就表示自动提交(也就是autocommit)开着注意:
InnoDB支持事务,MyISAM不支持事务
修改事务提交方式的方法:(这个是会话级的修改,退出当前
mysql会话就没了)
set autocommit=0;设置成禁止自动提交
set autocommit=1;设置成开始自动提交注意:需要重启
mysql客服端才会生效
关于单条SQL语句跟事务的关系:
在自动提交开启时,单条SQL语句其实就是一个事务,并且这个事务会立马自动提交
手动提交
需要用
begin手动开启一个事务commit来提交一个事务begin;//开启事务--或者用start transaction savepoint a;//设置一个保存点--名字叫a 保存点可以设置多个 rollback to a;//回滚到保存点a处--保存点到这的操作都会被抹除,这些操作都会失去作用 rollback;//直接回滚到开始事务的最初状态,并关闭事务 commit;//提交这个事务,事务自动关闭注意:如果一个事务已经提交了,就不能
rollback了
对异常情况的分析:
1.还没有
commit,客户端就崩溃了,MySQL会自动回滚到上一个commit2.已经
commit了,客户端崩溃,MySQL的数据不会受影响–因为持久化了3.如果本来是自动提交,但是手动启动事务的话,还是需要手动
commit才行
事务的隔离级别
数据库中,为了保证事务执行过程中尽量不受干扰–隔离性
数据库中,允许事务受不同程度的干扰–隔离级别
隔离级别有四种:
读未提交(
Read uncommitted 简称RU):所有的事务都可以看到其他事务没有提交的执行结果,会引发很多问题:不可重复读,脏读,幻读读提交(
Read committed 简称RC):一个事务只能看到其他的已经提交的事务所做的改变,会引起不可重复读,幻读问题可重复读(
Repeatable read 简称RR):一个事务里,反复读某条数据,不管中间有没有其他事务改了这数据,这条数据都不会受其他事务的影响,会引起比如幻读的问题–想收到其他事务的提交的话,必须要自己这个事务结束
–引申:在
RR级别下,多个修改操作的话,也是会加锁的串行化(
serializable):这个是事务的最高隔离级别。在每个读的数据行上面加上共享锁来强制事务排序从而避免相互冲突–但是可能会导致超时和锁竞争–如果几个事务是读操作的话,是可以同时进行的
大多数数据库的默认隔离级别是读提交
MySQL的默认隔离级别是可重复读
引申:事务在进行增删改操作时,都会偷偷的进行读取数据的
脏读:一个事务在执行中,读取到其他未提交事务的操作结果
不可重复读:一个事务在不同时间读取到的结果不同
–这是个问题,比如:先读取
月薪3k的,再读取月薪4k的,可能有人工资在这个期间被修改了,那这个人就出现了两次了幻读:大多数情况是因为隔离不了
insert插入数据导致的–隔离不了
insert的原因:没有历史版本可以被锁定
查看和设置隔离级别
查看全局的隔离级别:select @@global.tx_isolation;
查看会话(当前)的隔离级别:select @@session.tx_isolation;或者select @@tx_isolation;
设置隔离级别的语法:
set [session | global] transaction isolation level
{read uncommitted | read committed | repeatable read | serializable};
[session| global]如果不选的话,默认是session
{}里面那个是多选一
eg:set session transaction isolation level read committed;
引申:如果修改了全局的话,重新连接会话,会话就变成现在全局的这个了
当前会话跟全局的隔离级别不一样的话,会以当前会话的隔离级别为准
注意:改隔离级别一般是选择改全局的–一般是要求隔离级别都一样会好些
更深层次的理解隔离性
数据库并发控制用的是
MVCC(多版本并发控制)–处理读-写和写-写的并发问题
MVCC是一种用来解决
读-写冲突的无锁并发控制–为事务分配事务ID,给每个修改操作都保存一个版本;读操作就是读这个事务开始前的快照
引申:事务ID是单增的
理解MVCC
前置知识引入:
3个记录隐藏列(隐藏字段)
undo日志Read View
3个记录隐藏列:
DB_TRX_ID:记录创建这条记录/最后一次修改该记录的事务ID
DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本
DB_ROW_ID:隐含的自增ID(即隐藏主键)–如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引如果不知道的话,会默认设置成
null引申:还有一个删除
flag隐藏字段, 记录这个数据是不是被delete
undo日志:就是MySQL中一段内存缓冲区,用来保存日志数据–事务提交之后,那个事务对应的在
undo日志里的数据会被标记为可回收(等其他事务的快照不用他了就会被清理)
单条数据的多版本链:(基于链表形成的历史版本链)
在修改之前,上一条数据会在
undo log里面形成一个副本(地址是0x18181818),修改是在非副本的那条数据上直接进行的修改–
update和delete可以形成版本链,insert先不考虑
delete的话就是给这个数据的隐藏列打上一个已删除的标记回滚其实就是用历史的数据去覆盖当前的数据(回滚回去了就不会回滚回来了)
事务在执行快照读时,会生成数据库当前的一个快照
快照读(也就是读取历史版本):比如在读已提交下,每次
select会触发快照读只有读已提交和可重复读才会有快照读
写-写并发的话都理解成是当前读(读取数据的最新版本)
Read View:事务进行快照读操作的时候产生的读视图,是一个类,里面主要有:
m_ids–一张列表–维护这个读视图生成时系统正常执行的事务ID
up_limit_id–记录m_ids列表中事务ID最小的ID
low_limit_id记录读视图生成时系统没有分配的最小的事务ID
creator_trx_id创建改读视图的事务ID快照就是记录快照时数据库里面数据当时的版本
Read View是实现快照这个功能的核心–看DB_TRX_ID跟Read View中那几个变量的关系来判断应该看到这个数据的哪个版本
作业部分
设有两个事务T1,T2,其并发操作如下所示,下面评价正确的是(D)
A.该操作不能重复读
B.该操作不存在问题
C.该操作读"脏"数据
D.该操作丢失修改


