第2节 Mysql事务及事务实现原理 2021-12-24

Java组件总目录



一、插入数据执行流程

在这里插入图片描述

二、事务概述

事务是数据库最为重要的机制之一,凡是使用过数据库的人,都了解数据库的事务机制,也对ACID四个基本特性如数家珍。在MySQL中的事务是由存储引擎实现的,而且支持事务的存储引擎不多,我们主要讲解InnoDB存储引擎中的事务。

数据库事务具有ACID四大特性。ACID是以下4个词的缩写:

  • 原子性(atomicity) :事务最小工作单元,要么全成功,要么全失败 。

  • 一致性(consistency): 事务开始和结束后,数据库的完整性不会被破坏。

  • 隔离性(isolation) :不同事务之间互不影响,四种隔离级别为RU(读未交)、RC(读已提交)、RR(可重复读)、SERIALIZABLE (串行化)。

  • 持久性(durability) :事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失。

三、事务的隔离级别

1、Read uncommitted(RU)
读未提交,一个事务a可以读取到事务b中未提交的数据。
最低隔离级别,相当于没有隔离。不要应用在生产环境。
出现脏读的问题。

2、Read Commited(RC)*
读已提交,一个事务a可以读取到事务b中已经提交的数据。
可以应用在生成环境,Oracle数据库默认的隔离级别就是RC。
现象:不可重复读
	在一个事务中执行相同的sql语句查询数据得到的结果可能是不同的。
	
3、REPEATABLE READ(RR)*
可重复读。MySQL默认事务隔离级别。解决了幻读的问题(使用间隙锁解决,后面详细讲解)。
在同一个事务中,相同的Select语句反复执行得到的结果是相同的。
可能会出现问题:幻读。为了实现可重复读,不能出现幻读。
	在同一个事务中,相同的Select语句反复执行可能查询到其他事务插入的数据。
可以应用在生成环境的。

4、SERIALIZABLE
所有操作串行执行,事务之间完全隔离。性能太差。不会应用在生成环境

四、事务和MVCC底层原理详解

1 思考:丢失更新

增加的100 块钱丢失问题, 事务无隔离。
在这里插入图片描述

2 解决方案

解决方案1:LBCC

LBCC,基于锁的并发控制,英文全称Lock Based Concurrency Control)可以解决上述的问题。查询对读取的行加锁,等到操作结束后再释放所有行上的锁。因为数据被锁,导致操作被阻塞,直到事务提交并将所有锁都释放 才可操作。

解决方案2:MVCC

MVCC,多版本的并发控制,英文全称:Multi Version Concurrency Control
机制可以解决这个问题。

MVCC使得数据库读不会对数据加锁,普通的SELECT请求不会加锁,提高了数据库的并发处理能力。借助MVCC,数据库可以实现READ OMMITTED,REPEATABLE READ等隔离级别,用户可以查看当前数
据的前一个或者前几个历史版本,保证了ACID中的I特性(隔离性)

3 InnoDB的MVCC实现

MVCC最大的好处,相信也是耳熟能详:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性
能,这也是为什么现阶段,几乎所有的RDBMS,都支持了MVCC。

多版本并发控制仅仅是一种技术概念,并没有统一的实现标准, 其核心理念就是数据快照,不同的事务访问不同版本的数据快照,从而实现不同的事务隔离级别。虽然字面上是说具有多个版本的数据快照,但这并不意味着数据库必须拷贝数据,保存多份数据文件,这样会浪费大量的存储空间。

InnoDB通过事务的undo日志巧妙地实现了多版本的数据快照。

MVCC 在mysql 中的实现依赖的是 undo logread view

1 undo log

根据行为的不同,undo log分为两种: insert undo log 和 update undo log。

insert undo log 不存在历史数据,可以在事务提交后直接删除而不需要进行 purge 操作。

为了更好的支持并发,InnoDB的多版本一致性读是采用了基于回滚段的的方式。另外,对于更新和删除操作,InnoDB并不是真正的删除原来的记录,而是设置记录的delete mark为1。因此为了解决数据Page和Undo Log膨胀的问题,需要引入purge机制进行回收。

回滚日志,能够记录数据在修改之前的样子。以记录为单位。
insert undo log
插入数据:生成一个新的记录。

update undo log:
  删除记录:在对应的记录上打上删除标记即可。
  更新数据:产生一个版本链。
	|事务id|回滚指针|rowid(主键)|记录的内容|
更新数据的版本链结构

在这里插入图片描述
在这里插入图片描述

2 read view

控制版本的读取
1)readview生成的时机
	在事务中执行select操作,就会生成readview。
2)创建ReadView的过程
	就是一个事务id的区间。
	上限:数据库中即将生成的下一个事务id。
	下限:当前数据库中活跃的事务id列表的最小值。
3)判断可见性
	可见说明可访问该版本,不可见不可访问。
	1、如果版本中的事务id小于ReadView的最小值那么就是可见的。
	2、如果版本中的事务id大于等于ReadView的最大值,那么就是不可见的。
	3、如果版本中的事务ID大于最小值,小于最大值,需要判断事务id是否在m_ids中存在,如果存在的话不可见,如果不存在就可见。
如果存在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;
如果不存在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。
ReadView 的主要变量
  • m_ids : 活跃的读写事务的事务id列表
  • m_up_limit_id:m_ids事务列表中的最小事务id,如果当前列表为空那么就等于m_low_limit_id。事务id的下限。
  • m_low_limit_id:系统中将要产生的下一个事务id的值。事务id的上限。
  • m_creator_trx_id:当前事务id,m_ids中不包含当前事务id。
不同事务隔离级别的实现方式
  • 对于使用 READ UNCOMMITTED 隔离级别的事务来说,直接读取记录的最新版本就好了。
  • 对于使用SERIALIZABLE 隔离级别的事务来说,使用加锁的方式来访问记录。
  • 对于使用 READ COMMITTED 和 REPEATABLE READ 隔离级别的事务来说,就需要用到我们上边所说的 版本链

在 MySQL 中, READ COMMITTED 和 REPEATABLE READ 隔离级别的的一个非常大的区别就是它们生成ReadView 的时机不同,我们来看一下。

  • READ COMMITTED:每次读取数据前都生成一个ReadView
  • REPEATABLE READ:在事务开始后第一次读取数据时生成一个ReadView

对于使用 REPEATABLE READ 隔离级别的事务来说,只会在第一次执行查询语句时生成一个 ReadView ,之后的查询就不会重复生成了。

事务结束则ReadView 过期。

五、 MVCC下的读操作

在MVCC并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)。

1.当前读

读取的是记录的最新版本,并且当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。
特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。 加行写锁 读当前版本

# 对读取记录加S锁 (共享锁)
select * from table where ? lock in share mode; 
# 下面都是加的是X锁 (排它锁)
select * from table where ? for update; 
insert into table values (); update table set ? where ?; 
delete from table where ?;

2. 快照读

读取的是记录的可见版本 (有可能是历史版本),不用加锁。
简单的select操作,属于快照读,不加锁。

六、 事务回滚和数据恢复

事务的隔离性由多版本控制机制和锁实现,而原子性,持久性和一致性主要是通过redo log、undo log和Force Log at Commit机制机制来完成的。redo log用于在崩溃时恢复数据,undo log用于对事务的影响进行撤销,也可以用于多版本控制。而Force Log at Commit机制保证事务提交后redo log日志都已经持久化。

redo log

开启一个事务后,用户可以使用COMMIT来提交,也可以用ROLLBACK来回滚。其中COMMIT或者ROLLBACK执行成功之后,数据一定是会被全部保存或者全部回滚到最初状态的,这也体现了事务的原子性。

InnoDB通过Force Log at Commit机制实现事务的持久性,即当事务 COMMIT时,必须先将该事务的所有日志都写入到redo log文件进行持久化之后,COMMIT操作才算完成。

我们先来看一下redo log的原理,redo log顾名思义,就是重做日志。 Mysql 的日志系统,记录的是数据库页的物理修改操作,数据库可以根据redo log进行数据恢复。

undo log的存储不同于redo log,它存放在数据库内部的一个特殊的段(segment)中,这个段称为回滚段。回滚段位于共享表空间中。undo段中的以undo page为更小的组织单位。undo page和存储数据库数据和索引的页类似。因为redo log是物理日志,记录的是数据库页的物理修改操作。所以undo log(也看成数据库数据)的写入也会产生redo log,也就是undo log的产生会伴随着redo log的产生,这是因为undo log也需要持久性的保护。

数据库事务的整个流程

在这里插入图片描述
在事务执行的过程中,除了记录redo log,还会记录一定量的undo log。undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。

正常情况

事务进行过程中,每次sql语句执行,都会记录undo log和redo log,然后更新数据形成脏页,然后redo log按照时间或者空间等条件进行落盘,undo log和脏页按照checkpoint进行落盘,落盘后相应的redo log就可以删除了。

未提交崩溃

此时,事务还未COMMIT,如果发生崩溃,则首先检查checkpoint记录,使
用相应的redo log进行数据和undo log的恢复,然后查看undo log的状态发现事务尚未提交,然后就使用undo log进行事务回滚。

提交后崩溃

事务执行COMMIT操作时,会将本事务相关的所有redo log都进行落盘,只
有所有redo log落盘成功,才算COMMIT成功。然后内存中的数据脏页继续按照checkpoint进行落盘。

如果此时发生了崩溃,则只使用redo log恢复数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值