重点知识学习(9.3)--[浅入MySQL数据库事务,浅入MVCC]
Posted 小智RE0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重点知识学习(9.3)--[浅入MySQL数据库事务,浅入MVCC]相关的知识,希望对你有一定的参考价值。
文章目录
1. 事务
当时学习Spring的时候,也是学过一点事务的知识:
Spring框架学习笔记(3) — [在spring中初步上手实现AOP,以及对事务的初步配置使用]
Spring框架学习笔记(4) — [Spring的事务传播行为]
然后当时后面和小伙伴一块写项目的时候,对于事务方面处理的话,也仅仅是在对整个服务层进行事务声明,也是保证了数据操作的安全性.
本次还是得学学事务的知识,起码要明白一些专业术语的含义以及部分实践.
- 事务就是一次完整的数据库操作,该操作过程可能包含多条sql的执行.
- 要么都执行成功,要么都执行失败.
- 注意,mysql中目前仅InnoDB引擎支持事务.
- 事务主要是为了增删改操作,查询操作不会有太大问题的.
先简单熟悉一下操作;
(1)自动提交事务
开启自动提交
SET SESSION autocommit=1;
SET GLOBAL autocommit=1;
SESSION
表示会话;
GLOBAL
表示全局;
禁止自动提交
SET SESSION autocommit=0;
SET GLOBAL autocommit=0;
(2)手动提交事务
- 开启一个事务.
BEGIN;
# 或者
START TRANSACTION;
- 事务回滚.
ROLLBACK;
- 提交当前事务.
COMMIT;
试试吧;注意数据表的存储引擎是InnoDB
的;
先关闭自动提交;
#关闭自动提交;
SET GLOBAL autocommit=0;
查看当前的事务状态;
SHOW GLOBAL VARIABLES LIKE 'autocommit';
开启事务;执行语句;
#测试;
BEGIN;
执行一条sql;
INSERT INTO t_test(id,account) VALUES(1,'杰哥');
数据存入成功;
现在可以执行一下事务回滚试试;
ROLLBACK;
表数据没了
现在试试,开启事务,提交之后,是否还能回滚呢;
一行一行执行;
#测试;
BEGIN;
INSERT INTO t_test(id,account) VALUES(1,'杰哥');
#提交;
COMMIT;
#回滚;
ROLLBACK;
由于事务已提交,无法再回滚到空数据状态;
1.1 事务的四特性
ACID原则:
原子性/不可分开性
(Atomicity)一致性
(Consistency)隔离性/独立性
(Isolation)---- 重点学习部分持久性
(Durability)。
原子性:在一次事务过程中的多个执行操作,要么都成功,要么都失败
一致性:在事务开始之前和事务结束之后,我们的数据库完整性
没有被破坏.
比如说现在采用多种方式对银行账户的余额进行多次操作,那么最后账户里面余额应该是我们所预期的结果,绝对不能出现错误.不然就得出问题.
隔离性:在同一时间,数据库允许多个事务进行访问
, 那么就得对这些事务进行隔离.
关于隔离性,后面有四个级别可以看看.
持久性:事务一旦提交,咱们的数据就不可能改变
.就算数据库服务层出现了问题.
1.2 并发事务带来的问题
并发操作事务时,就可能出现 脏读,不可重复读,幻读…等问题.
(1)脏读
可以看看这个案例
事务B正在进行,还没来得及提交/回滚数据; 事务A抢先出手了,他把数据读取走了;
没想到事务B留了一手,它又回滚了,把数据恢复了;
嘿嘿,事务A读到的就是垃圾数据.
当前事务读取到其他事务未提交的数据
;读取到垃圾数据.
(2)不可重复读
看这个案例
事务A这次比较聪明,快速把数据读取了一下, 注意此时事务A读到的年龄数是20
;
事务B也要出手了,它先把数据更新了一下,然后提交数据;
事务A又出手了;它又去读取数据,结果读取到了年龄数为22
.
事务A想不明白,就在自己这一个事务中进行了两次读取操作,按理说读取到的不应该数据一致吗?
A事务开启后 读取两次数据,结果两次读到的内容不一样( 预期的效果是A在同一个事务中读取到数据应该是一样的)
(3)幻读
看这个案例.
事务A先去读取了年龄大于18岁的数据; 读取到了一行;注意事务A还没结束;
这时事务B出手了,它添加了一行数据; 然后提交.
事务A又去读取年龄大于18岁的数据,读取到了两行,
哎呀,事务A出现幻觉了,咋在一次事务中读取到了两个不同的呢.
幻读可以这么理解.
A事务开启后, 读取到的两次数据数量不一致
1.3事务的4个隔离级别
查看隔离级别
SELECT @@global.transaction_isolation,@@transaction_isolation;
设置隔离级别
#这里SESSION(设置当前会话)/GLOBAL(设置全局) 设置一个就行
SET SESSION/GLOBAL TRANSACTION ISOLATION LEVEL 隔离级别
-
读 ---未提交
(read uncommitted):A 事务可以读到B事务未提交的数据 ;会有脏读,幻读不可重复读问题
,几乎不用这个. -
读---已提交
(read committed): A不能读到B未提交的数据,只能读到B已提交的数据; 可解决脏读问题, 但是解决不了不可重复读问题
. -
可重复读
(repeatable read): A事务开启后,第一次读到某个数据后,那么在这个事务中,第二次再查询同样的数据时,和原来是一致,属于重复读, (咱们数据库默认用的就是可重复读级别
,注意下,咱们用的mysql8已经解决了幻读问题).
[这边重复读可以理解为一种快照机制,它会把第一次读取的数据拍照存储,后面读取的时候就不是在数据库读取,而是读取快照中的存储信息,一会可以了解一下MVCC机制
] -
串行化
(serializable): 解决所有问题, 一次只允许一个事务进行操作; 是最安全的,效率是最低的.
实际操作看看吧;
# 查看隔离级别;
SELECT @@global.transaction_isolation,@@transaction_isolation;
(1)试试读--未提交
案例
(1)首先试试 读--未提交
隔离级别
搭建一个脏读案例
首先将当前数据库设置为读未提交级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
开两个命令窗口;暂且称呼左边为事务A;右边为事务B;
在数据表t_user
进行操作;
首先开启事务A,开启事务B;
然后在事务B中添加一行数据,然后在事务A中读取数据;
注意啊,事务B还没有结束啊;这时事务A就跑去读取数据了;
然后事务B回滚操作了; 这属于是脏读
案例了;
事务A中读取数据,没读到
(2)试试读--已提交
案例
(2)试试 读--已提交
隔离级别
搭建不可重复读案例
首先将当前数据库设置为读,已提交级别
#设置为 读 已提交级别;
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
同样地,开启事务A,开启事务B;
首先在事务B中添加数据;但是没有提交;
然后在事务A中查询读取数据,并没有读取到数据;(这就是读-已提交
级别的魅力)
首先,事务B提交;
这时事务A才能读取到数据;
虽然说解决了脏读的问题;但是这里出现了不可重复读
的问题;
嘿嘿,这个案例还可以继续试试,加强理解;
开启事务A,读取数据表的数据;(注意小智的年龄是22岁)
开启事务B;
然后在事务B中修改了数据,然后进行了提交;
然后在事务A中再次读取数据;发现数据不一致了;
也就是不可重复读
问题;
(3)试试可重复读
案例
(3)试试 可重复读
隔离级别
嘿嘿,这可是数据库的默认隔离级别哦
先设置为可重复读
的隔离级别;
#设置为 可重复读级别;
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
当然,开启事务A,开启事务B;
事务A中; 先看下数据表t_user
的当前数据;
然后这时,在事务B中执行修改数据; 将小智修改为100岁;
进行事务B提交;
然后,注意啊,咱们这时候呢,事务A还没结束呢;
咱们让事务A再次读取数据;发现小智的年龄还是原来的数据 23岁;
也就是说,这可是解决了不可重复读的问题哦,
这就是可重复读级别的
厉害之处.
好了,现在把事务A提交了,读取数据;
已经是修改后的数据了.
当然,再试试实践,看看是不是解决了
幻读问题
;
开启事务A,开启事务B;
在事务A中首先读取数据;
注意读取到了一行数据;
然后在事务B中执行一个添加数据;并没有提交;
这时在事务A中;再次查询数据;注意读取的数据行为1行;
这时提交事务B;
在事务A中,再次查询数据,还是一行数据;
然后提交事务A;
开启一个新事务,查询读取数据;这才读取到两行数据;
幻读问题解决了;
2. 浅入MVCC(多版本并发控制 Multi-Version Concurrent Control)
,配合 Undo log 和版本链,替代锁,让不同事务的读-写、写-读操作可以并发执行,达到提升系统性能的效果.
可提升读-写
,写-读
两个操作同时进行
注意:写-写 操作互斥
mysql支持行级锁的,(行级锁下篇笔记再记录);
好家伙,要是多个操作同一个数据,那不行啊,要出大问题的;
MVCC常用于: 读已提交(READ COMMITTED 和 可重复读(REPEATABLE READ)隔离级别的事务
比如咱们两个事务,一个读一个取是没问题的;
要是同一时间一起写,就有大问题了;
版本链问题
InnoDB 存储引擎
数据表的聚簇索引记录中
包含着必须隐藏列:
trx_id
:当我们对某条记录进行改动时,会把对应的事务 id
赋值给trx_id 隐藏列
。
roll_pointer
:
每次对表中的记录操作时,会保存到日志(undolog) 里面
;会记录当前事务的id号,就像是一个指针
;后面可以通过记录查询到修改前的信息。如果有多个事务操作时,他们就会根据事务id,找到自己操作的版本记录.
注意:每行数据都存在一个版本,每次数据更新时都更新该版本, 然后在事务中进行数据更新时,首先会复制一份版本记录,里面记录修改的数据版本记录;(感觉就像版本控制工具的分支);
最终;要是提交了事务就会覆盖原记录;要是回滚事务就删掉执行的记录版本.
每次更新版本记录后,就会把将旧版本的值放到一条 undolog 中
,跟随着更新的次数叠加;所有的版本都会被 roll_pointer 属性
连接成一个链表,就是版本链.
- 每个版本节点中还会包括
该版本时对应的事务 id
,明白是哪个事务修改的版本; - 版本链的头节点就是当前记录最新的值。
READ COMMITTED(读已提交):每次读取数据前都生成一个 ReadView(临时读视图)
,
由于读已提交会产生不可重复读问题
, 只要事务的中数据发生改变,版本链中也会发生修改
, 每次读的时候ReadView中的数据就发生改变
.
REPEATABLE READ(可重复读):在第一次读取数据时生成一个 ReadView(临时读视图)
,在后面就算数据或者版本链发生变化
也没事,因为第一次读的时候,已经做好拍照了,当前事务中,要是再查数据,就以快照中的数据为准.
ReadView (临时读视图) 主要包含
当前系统中还有哪些活跃的读写事务
,会将事务 id 存入列表m_ids
中 。
所以呢,在开启一次会话进行 SQL 读写时,开始事务就生成ReadView
,把当前系统中正在执行的写操作存入到m_ids 列表
.
以上是关于重点知识学习(9.3)--[浅入MySQL数据库事务,浅入MVCC]的主要内容,如果未能解决你的问题,请参考以下文章
重点知识学习(9.4)--[浅入MySQL数据库锁机制以及SQL优化]
重点知识学习(9.2)--[MySQL数据库索引,浅入数据库引擎]