【MySql】- 2.MySQL的事务2-隔离级别

1.事务的4个特性

在聊事务的隔离级别之前我们先回顾以下事务的特性。

  1. 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
  2. 一致性:执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
  3. 隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  4. 持久性:一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

b.并发事务带来的问题

在我们程序中使用事务时,通常都是并发进行的,而虽然并发是必须的,但是也会带来以下问题:

  • 脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
  • 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
  • 不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
  • 幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

不可重复的与幻读的区别:
不可重复的的重点是数据的修改,而幻读的重点是增加或删除记录。

为了解决上述的问题,数据库就引入了隔离级别的概念:

2.事务的隔离级别

事务有4个隔离级别

a. 读未提交(read uncommit)

一个事务还未提交被其他事务读取到,会产生脏读

b.对已提交(read commit)

一个事务可以读取到另一个事务已经提交的数据,而另一个事务对数据的每次修改提交后,该事务都可以查询到最新值。可以解决脏读,但会产生不可重复读,幻读的情况。

c.可重复读(repeatable read)

事务在读取某条记录后,即使其他的事务修改了这条数据,并且提交了,该事务在再次查询之前读取的记录时,读到的仍然是第一次读取到的数据。这就是可重复读,这种隔离级别解决了不可重复读,但还是会出现幻读。

d.串行化(serializable)

以上3种隔离级别都允许对同一条记录同时进行读-读、读-写、写-读的并发操作,如果我们不允许读-写、写-读的并发操作,可以使用SERIALIZABLE隔离级别,这种隔离基金因为对同一条记录的操作都是串行的,所以不会出现脏读、幻读等现象

e.总结

  • READ UNCOMMITTED隔离级别下,可能发生脏读 脏读、 不可重复读和和幻读问题。
  • READ COMMITTED隔离级别下,可能发生不可重复读 不可重复读和幻读问题,但是不会发生 问题,但是不会发生脏读问题。
  • REPEATABLE READ隔离级别下,可能发生幻读问题,不会发生脏读和不可重复读的问题。
  • SERIALIZABLE隔离级别下,各种问题都不可以发生。
  • 注意:这四种隔离级别是SQL的标准定义,不同的数据库会有不同的实现,特别需要注意的是MySQL在在REPEATABLE READ隔离级别下,是可以禁止幻读问题的发生的

3.隔离级别的实现

前三个隔离级别是基于以下两个东西实现的:

a.版本链

对InnoDB存储引擎的表来说,它的每一个聚簇索引记录都会包含两个必要的隐藏列(row_id并不是必要的,我们创建的表中有主键或者非NULL唯一键时都不会包含row_id列

  • trx_id:每次对某条记录修改时,都会的事务的id赋值给trx_id隐藏列
  • roll_pointer:每次对某条记录进行改动时,这个隐藏列会存一个指针,可以通过这个指针找到该记录修改前的信息。

b.ReadView

对于使用READ UNCOMMITTED隔离级别的事务来说,直接读取记录的最新版本就好了,对于使用SERIALIZABLE隔离级别的事务来说,使用加锁的方式来访问记录。对于使用READ COMMITTED和REPEATABLE READ隔离级别的事务来说,就需要用到我们上边所说的版本链了,核心问题就是:需要判断一下版本链中的哪个版本是当前事务可见的

ReadView中主要包含4个比较重要的内容:

  1. m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
  2. min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小
    值。
  3. max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。
  4. creator_trx_id:表示生成该ReadView的事务的事务id。

注意max_trx_id并不是m_ids中的最大值,事务id是递增分配的。比方说现在有id为1, 2, 3这三个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时, m_ids就包括1和2, min_trx_id的值就是1,max_trx_id的值就是4。

如何判断某个记录对当前事务可见

有了上面的ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:

  • 如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
  • 如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
  • 如果被访问版本的trx_id属性值大于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
  • 如果被访问版本的trx_id属性值在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。

例如有下面的版本链:
在这里插入图片描述

如果有事务A(id = 100,读),事务B(id = 200,写);
若当前ReadView是* m_ids:[100,200].那么A事务只能读82这条记录。

Read Commited的实现方式

每次读取前生成一个ReadView;

例如,在A第一次读数据时B如果未提交那么就会生成[100,200]表示100 -》200的记录是活跃的,不可读,而当A第二次访问数据时,如果B提交了,它就会生成[100]此时记录为200的记录不在活跃范围内,就可以被读取。

Repeatable Read的实现方式

只在第一次读取时生成ReadView;

例如在A第一次读的时候B未提交,此时会生成[100,200]的ReadView,在A第二次读时,B已经提交,但是此时ReadView不会更新,还是[100,200]200的记录仍然不会被读取,这就是可重复读的实现方式

4.MVCC总结

上述版本链就是Mvcc,

MVCC( Multi-Version Concurrency Control ,多版本并发控制)指的就是在使用READ COMMITTD、REPEATABLE READ这两种隔离级别的事务在执行普通的SEELCT操作时访问记录的版本链的过程。可以使不同事务的读-写、写-读操作并发执行,从而提升系统性能。

READ COMMITTD、 REPEATABLE READ这两个隔离级别的一个很大不同就是:生成ReadView的时机不同, READ COMMITTD在每一次进行普通SELECT操作前都会生成一个ReadView,而REPEATABLE READ只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复使用这个ReadView就好了。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值