当前位置:   article > 正文

事务隔离级别——未提交读、已提交读、可重复读、串行_未提交读 已提交读

未提交读 已提交读

事务隔离级别——未提交读、已提交读、可重复读、串行

事务隔离级别是指多个事务之间,不同事务中涉及的读写操作互相影响的隔离。其中多个事务中同时对同一条数据或者表进行写操作(insert、update、delete),必定会导致事务间数据的相互影响,导致不可预知问题发生。主要的问题可以归为以下几种:

  1. 脏读——A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。
  2. 不可重复读——事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。
  3. 幻读——事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。
    下面举几个例子重现一下这几种问题现象,更好的理解一下。

重现问题

  1. 打开两个mysql客户端
  2. 设置事务隔离级别为未提交读;关闭自动提交;
set session transaction isolation level read uncommitted;
set session autocommit=0;
  • 1
  • 2

建表语句:

create table tbl_transaction( column1 char, column2 char);
insert into tbl_transaction values ('a', 'b');
  • 1
  • 2

初始数据:
初始数据

脏读

客户端1客户端2
begin;begin;
update tbl_transaction set column1=‘b’;
select * from tbl_transaction;
结果1
读取到了事务1未commit的修改
rollback;
select * from tbl_transaction;
结果2
当客户端1回滚后读取到回滚后的数据
commit;

不可重复读

当我们把隔离级别设置为:

set session transaction isolation level read committed;
  • 1
客户端1客户端2
begin;begin;
update tbl_transaction set column1=‘b’;
select * from tbl_transaction;
结果1
事务2没有读取到事务1的修改,脏读问题解决了
commit;
select * from tbl_transaction;
结果2
当事务1commit之后,事务2可以读取到相关的修改
commit;

可以看到在事务1commit之前,事务2都无法看到事务1做出的修改。
这种级别为:提交读
但是这里又出现了另一个问题:事务2中同一条数据查询两次的查询结果都不一样,对于同一条数据不可重复读,这就是不可重复读问题。
提交读隔离级别解决了未提交读的问题,但是不能解决不可重复读问题。
怎么解决不可重复度问题呢?提供几个思路:

  1. 事务2在读这条数据的时候判断数据有没有被其他事务给修改,如果已经被其他事务(修改)占有了,那么等待,直到占有数据的事务结束;如果没有其他事务占有,则把数据锁定。类似与读写锁的实现。
  2. 事务2中作读操作的时候,只查询在事务2发起读请求前修改的记录,不管后面修改了多少次,事务2只管起事务前的修改。
    在innodb中选择了第二种方案,采用了MVCC的策略,利用delete_version、create_version两个版本号,在事务中查询只能查到满足:
    create_version<=事务ID<=delete_version(或者为空)
    条件的记录。

幻读

当我们把隔离级别设置为:

set session transaction isolation level read committed;
  • 1
客户端1客户端2
begin;
begin;
select * from tbl_transaction;
结果1
insert into tbl_transaction values(‘c’, ‘d’);
commit;
select * from tbl_transaction;
结果1
事务2没有读取到事务1的插入,没出现幻读的现象

事务1的事务ID为ID1
事务2的事务ID 为ID2,其中ID2<ID1(事务ID分配并非在事务begin的时候分配,是执行Select的时候才分配的)
新插入的数据:

column1column2create_versiondelete_version
‘c’‘d’ID1null

由于ID2<ID1,不满足create_version<=事务ID条件,因此新插入的数据没有被查出啦。
那啥时候会出现幻读的现象呢?可以从下面的例子:

客户端1客户端2
begin;
begin;
select * from tbl_transaction;
结果1
insert into tbl_transaction values(‘c’, ‘d’);
commit;
select * from tbl_transaction;
结果1
事务2没有读取到事务1的插入,没出现幻读的现象
update tbl_transaction set column2=‘c’ where column1=‘c’;
update结果
再次select:幻读会发现多了一条数据,出现了幻读。

那么遇到幻读现象怎么解决呢?

  1. 最简单的方法是把事务隔离级别设置为串行,也就是说当事务2未结束时,事务1不能执行;
  2. 事务2在select的时候把表给锁了,不让任何插入行为发生,直到事务2执行commit之前,事务1insert操作阻塞
  3. 不对整张表锁,只锁住select的部分表空间,比如说select * from xxx where column1 > 100,那么只需要把100以上的表空间锁了就行,这个称为间隙锁。可以参考这篇文章:https://blog.csdn.net/weixin_43258908/article/details/89076669
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/239559
推荐阅读
相关标签
  

闽ICP备14008679号