mysql事务

1、隔离级别

读未提交:别人改数据的事务尚未提交,我在我的事务中也能读到。
读已提交:别人改数据的事务已经提交,我在我的事务中才能读到。
可重复读:别人改数据的事务已经提交,我在我的事务中也不去读。
串行:我的事务尚未提交,别人就别想改数据。
这4种隔离级别,并行性能依次降低,安全性依次提高。

2、对应出现的问题:

1 脏读

脏读发生在一个事务A读取了被另一个事务B修改,但是还未提交的数据。假如B回退,则事务A读取的是无效的数据。这跟不可重复读类似,但是第二个事务不需要执行提交。

2 不可重复读:

是指事务中,对于同一条数据,两次查询的结果不一致,原因,在查询的过程中,其他事务做了更新的操作。多指更新操作

将当前事务隔离级别设置为读已提交

1 现有一个学生表,有俩个字段(name,age)
2 插入数据insert into student(name,age) values('1',1)
3 新建俩个事务 tx1 tx2 
4 tx1 进行查询 select age from student where name = '1'   ## age 为 1
5 tx2 执行更新语句 update student set age = 2 where name= '1'
6 tx2 提交
7  tx1在进行查询  ## age 为2


结论:在同一个事务中,俩次查询的结果不一致,导致不可重复读。


3 幻读:

幻读是指在俩次快照读中间,有过一次或多次的修改,并且这次修改是其他事务已经提交并修改过程的数据或者是当前读,会导致俩次快照读不一致。

修改或者删除会导致新添一个undo log 版本链,所以当前事务在MVCC规则下,快照读可以看到修改后的数据。

对于约束性规则,如插入唯一索引的时候,也会进行当前读。

比如说我们有一个user 表,其中user_name 是唯一索引,进行如下操作。事务隔离级别为可重复读

例1

事务1事务2

begin;

insert into user(user_name) values (‘张三');

begin;

select * from user where user_name = '张三'

此时返回时空的

然后我们进行数据的插入;

insert into user(user_name) values (‘张三'); // insert 需要进行当前读

这时会报错,唯一索引重复了

例2

事务1事务2
begin;begin

select * from user where user_name = '张三';  // age = 10 ; 

select * from user where user_name = '张三'  // age 10

update user set age = age + 1 ;

commit ; 

update user set age = age + 1 ;  

select * from user where user_name = '张三'  // age 12

解决:通过间隙锁来解决,比如说在例子二将查询语句改为这样,select * from user where user_name = '张三' for update ,那么在 事务1更新年龄的时候就会阻塞。

3 mysql 事务查询:

select * from information_schema.innodb_trx 

4 mysql 事务的实现

事务实现是通过MVCC(多版本并发控制)来创建视图实现的。

这里的视图和表对应的视图不太一致

在 MySQL 里,有两个“视图”的概念:

  • 一个是 view。它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是 create view … ,而它的查询方法与表一样。
  • 另一个是 InnoDB 在实现 MVCC 时用到的一致性读视图,即 consistent read view,用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。

它没有物理结构,作用是事务执行期间用来定义“我能看到什么数据”。

对于可重复读来说视图的启动时机不同:

begin/start transaction
start transaction with consistent snapshot
第一种启动方式,一致性视图是在第执行第一个快照读语句时创建的;
第二种启动方式,一致性视图是在执行 start transaction with consistent snapshot 时创建的。

对于读已提交的话,是在每次执行一个SQL的时候创建一个视图

视图的实现:

InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。

而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。

DATA_TRX_ID:最近更新这条行记录的事务ID,6Byte
DATA_ROLL_PTR:表示指向该行回滚段(rollback segment)的指针,大小为 7 Byte
每次对数据进行更新操作时,都会 copy 当前数据,保存到 undo log 中。并修改当前行的回滚指针指向 undo log 中的旧数据行。
DB_ROW_ID:隐藏主键,6Byte
 

数据版本生成情况:

  • SELECT InnoDB只查找版本早于当前事务版本的数据行(也就是,行的事务编号小于或等于当前事务的事务编号)
  • INSERT InnoDB为新插入的每一行保存当前事务编号作为行版本号。
  • DELETE InnoDB为删除的每一行保存当前事务编号作为行删除标识。
  • UPDATE InnoDB为插入一行新记录,保存当前事务编号作为行版本号,同时保存当前事务编号到原来的行作为行删除标识。

也就是说,数据表中的一行记录,其实可能有多个版本 (row),每个版本有自己的 row trx_id。    InnoDB的MVCC实现依赖 undo log 和 read view
update 或 delete 操作中产生的 undo log。因为会对已经存在的记录产生影响,为了提供 MVCC机制,因此update undo log 不能在事务提交时就进行删除,而是将事务提交时放到入 history list 上,等待 purge 线程进行最后的删除操作。

图中虚线框里是同一行数据的 4 个版本,当前最新版本是 V4,k 的值是 22,它是被 transaction id 为 25 的事务更新的,因此它的 row trx_id 也是 25。图中的三个虚线箭头,就是undo log ,可以利用它进行事务的回滚。

接下来具体看下视图的生成(一下为可重复读的分析,读已提交类似,只是视图生成的时机不同):

按照可重复读的定义,一个事务启动的时候,能够看到所有已经提交的事务结果。但是之后,这个事务执行期间,其他事务的更新对它不可见。

因此,一个事务只需要在启动的时候声明说,“以我启动的时刻为准,如果一个数据版本是在我启动之前生成的,就认;如果是我启动以后才生成的,我就不认,我必须要找到它的上一个版本”。

当然,如果“上一个版本”也不可见,那就得继续往前找。还有,如果是这个事务自己更新的数据,它自己还是要认的。

在实现上, InnoDB 为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活跃”的所有事务 ID。“活跃”指的就是,启动了但还没提交。

数组里面事务 ID 的最小值记为低水位,当前系统里面已经创建过的事务 ID 的最大值加 1 记为高水位。

这个视图数组和高水位,就组成了当前事务的一致性视图(read-view)。

而数据版本的可见性规则,就是基于数据的 row trx_id 和这个一致性视图的对比结果得到的。

这个视图数组把所有的 row trx_id 分成了几种不同的情况。

这样,对于当前事务的启动瞬间来说,一个数据版本的 row trx_id,有以下几种可能:

  1. 如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;

  2. 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;

  3. 如果落在黄色部分,那就包括两种情况
    a. 若 row trx_id 在数组中,表示这个版本是由还没提交的事务生成的,不可见;
    b. 若 row trx_id 不在数组中,表示这个版本是已经提交了的事务生成的,可见。

一个数据版本对于一个事务视图来说有三种情况:

  1. 版本未提交,不可见;

  2. 版本已提交,但是是在视图创建后提交的,不可见;

  3. 版本已提交,而且是在视图创建前提交的,可见。

所以当前会话要想知道我能查询到什么数据,他会沿着这条线从后往前找,直到找到符合的。但是对于更新操作就不一样了,是当前读(总是读取已经提交完成的最新版本),更新完之后,将行的row trx_id 设为当前事务的id

除了 update 语句外,select 语句如果加锁,也是当前读

一致读:即读取当前可见的版本。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值