MySQL之事务

并发事务问题

在多个事务并发运行时,会遇到操作相同的数据来完成各自任务,可能会存在以下问题:

脏数据(Dirty READ)

当一个事物正在访问数据并且对数据进行修改,而这个操作还没有提交到数据库中,这使另外一个事务也访问了这个数据,然后使用了该数据,因为数据还没有提交到数据库,那么另一个事务读到的这个数据就是脏数据,依据脏数据的所有操作是不正确的。
示例:
在这里插入图片描述
总结:A事务读取到B事务尚未提交的更改数据,并在这个脏数据的基础上进行操作,如果B事务进行回滚,A事务读取到的事务是不被承认的。

不可重复读(UnrepeatableRead)

A事务内多次读取同一个数据,在这个事务还没结束时,B事务也访问该数据,A事务在两次读数据之间,B事务修改了该数据,A事务再次读取数据可能可第一次读取的不太一样。一个事务内两次读取的数据是不一样的,因此称为不可重复读。
示例:
在这里插入图片描述
在这里插入图片描述
总结:一个事务内两次读取的同一个数据是结果不一样的。不可重复读是指A事务读取到了B事务已经提交的更改数据。

幻读(phantom Read)

A事务读取几行数据,接着B事务插入了一些数据,在随后的查询中,A事务会发现一些原来不存在的记录,好像发生了幻觉一样,所以称为幻读。
示例:
在这里插入图片描述
总结:A事务读取到B事务提交的新增数据,幻读一般出现在数据统计的事务中。

不可重复读和幻读区别:

不可重读一般针对的是行级别的数据的更改,重点是修改数据。
幻读一般针对表级别的新增/删除数据,在统计事务中,两次读取的数据统计不一样,重点在于新增或者删除。

事务隔离级别

SQL标准定义了4种隔离级别:

1、读取未提交:Read-Uncommitted

最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读和重复读。

2、读取已提交:Read-Committed(RC)

允许读取并发事务中已经提交的数据,可以阻止脏读,但是幻读或不可重复读任然可能发生。

3、可重复读:Repeatable-Read(RR)

对同一个字段的多次读取结果都是一致的,除非数据被被事务自身修改。可以阻止脏读和不可重复读,但幻读仍可能发生。

4、串行化:Serializable

最高隔离级别,完全服从ACID的隔离级别,所有的事务依次逐个执行,这样事务之间就完全不可能出现干扰,即串行化可以防止脏读、不可重复读和幻读。
在这里插入图片描述
通过select @@tx_isolation可以查看隔离界别:
在这里插入图片描述
默认的隔离级别为可重复读。

事务的隔离级别的实现

读取已提交和可重复读采用的是排他锁(行锁)
读取已提交隔离级别是在读(写)之前加锁,在读(写)之后解锁,而不是等事务结束解锁,存在不可重复读的问题。
可重复读隔离级别整个事务都处于加锁状态下,只有事务结束才解锁,但还存在幻读,因为使用的是行锁,可以增加行等一些行为。
串行化使用的是表锁,锁定的是整张表才能解决幻读问题。

事务原理

1、MVCC多版本控制机制

MVCC(MultiVersion Concurrency Control),即多版本控制技术。
就是将行锁和行的多版本机制结合,只需要很小开销,就能实现事务,能够提高数据库的并发性能。
MVCC提供了另一种方式来实现读取已提交和可重复读的,相比于锁的方式效率会更高一些。
MVCC实现原理
对于数据表的每一条数据的记录
在这里插入图片描述
● DATA_TRX_ID:标记最新更新这条记录的transactionID,每处理一个事务,其值自动加1,大小是6字节。
● DATA_ROLL_PTR:指向当前记录项的rollback segment的undo log记录,找到之前版本的数据就是通过这个指针。
● DB_ROW_ID:行标识(隐藏自增ID),在INNODB的自动产生聚集索引时,聚集索引包括这个DB_ROW_DB的值,否则聚集索引中是不包含这个值,这个用在索引当中。
当针对一条数据记录执行update操作是,操作如下
● 针对表的记录加入排它锁
● 把这条记录的最新记录拷贝到undo log日志中,DATA_TRX_ID和DATA_ROLL_PTR不变
● 通过修改生成新的记录,并包含新的DATA_TRX_ID和DATA_ROLL_PTR
● 通过往复操作就会针对该记录形成一个版本链
在这里插入图片描述
当多个事务执行并发操作时,不同的隔离级别处理也不相同
● 读未提交:直接读取新版本链最新记录
● 串行化:通过互斥锁来访问数据
● 读已提交和重复读,通过基于版本链的ReadView

ReadView:在执行查询SQL会生成readView,它是由所有未提交的事务ID数组(最小 min_id)和已创建事务id(max_id)组成,形成的版本链的对比规则。
如果tid<min_id,表示这个版本事务是已经提交的,则数据可见。
如果tid>max_id,表示这个版本是将来启动的事务,是不可见的。
如果min_id<=tid<=max_id,有两种情况,如tid在数组中 ,则事务是活跃的,不可读取,则通过undo log找上个版本,如果tid不在数组中,则找到已提交事务。

不同的隔离级别区别在于生成的READView的时间点不同:
读已提交:一个是事务中每条select语句开始
可重复读:一个是事务之后第一个查询语句开始

MVCC特点:
1、每行数据都存在一个版本,每次数据更新时都会更新该版本。
2、修改时复制出当前版本随意修改(副本),各个事务之间互不干扰。
3、保存时比较版本号,如果成功,则覆盖原记录,失败则放弃copy(rollback)
4、事务是以排它锁的形式来修改原数据。
5、将修改前的数据存放在undo log,通过回滚指针与主数据相关联。
6、修改失败,则通过undo log 中数据进行恢复。

2、事务日志

在MVCC中,要保证事务的执行用到了redo log和undo log,需要通过事务日志来做跟踪进而保证事务的特性ACID。
每一个操作在真正写入数据库之前,先写入到日志文件中,如果要删除一样会先到日志文件中将对应行标记为删除,但数据库并没有变化,只有在整个事务提交时,再把整个事务中SQL语句批量同步到磁盘的数据库文件,每一次写操作都需要执行两遍:
1、先写入日志文件中,仅仅是操作过程,而不是将操作数据本身,所以速度比写数据库文件速度要快。
2、然后在写入数据库文件中,写入数据库文件的操作是重做事务日志中已经提交的事务操作记录。

redo log

在INNODB的存储引擎中,事务日志通过重做(redo log)和INNODB存储引擎的日志缓冲来实现。
事务开启时,事务中的操作,都会先写入存储引擎的日志缓存里,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化。
在这里插入图片描述

undo log

undo log主要为事务回滚服务。
在事务执行的过程中,除了记录redo log,还会记录一定量的undo log,undo log 记录数据在每个操作前的状态,如果事务在执行过程中需要回滚,就可以根据undo log进行回滚操作,单个事务的回滚只会去回滚当前事务做的操作,并不会影响其他的事务的操作。

redo log保证事务的持久性和一致性,而undo log则保证事务的原子性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值