mysql mvcc

2021-12-15  本文已影响0人  shark没有辣椒

mysql隔离级别

介绍隔离级别之前,我们先看看隔离级别需要解决的问题:

事务隔离级别,就是为了解决上面几种问题而诞生的。事务隔离级别越高,在并发下会产生的问题就越少,但同时付出的性能消耗也将越大。SQL 标准定义了四种隔离级别,这四种隔离级别分别是:

mvcc

介绍完隔离级别,我们来看MVCC,即Multi Version Concurrency Control的简称,代表多版本并发控制,用于支持RC(读已提交)和RR(可重复读)隔离级别的实现。在一个支持MVCC的并发系统中, 我们需要支持两种读, 一个是快照读, 一个是当前读。

MVCC最大的优势:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能。

MYSQL 事务日志

事务日志可以帮助提高事务的效率。使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝(可理解为缓存),再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。事务日志采用的是追加的方式,因此写日志的操作是磁盘上一小块区域内的顺序I/O,而不像随机I/O需要在磁盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快得多。事务日志持久以后,内存中被修改的数据在后台可以慢慢地刷回到磁盘。目前大多数存储引擎都是这样实现的,我们通常称之为预写式日志(Write-Ahead Logging),修改数据需要写两次磁盘。
如果数据的修改已经记录到事务日志并持久化,但数据本身还没有写回磁盘,此时系统崩溃,存储引擎在重启时能够自动恢复这部分修改的数据。

MySQL Innodb中跟数据持久性、一致性有关的日志,有以下几种:

MVCC实现

MVCC是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的删除时间。当然存储的并不是实际的时间值,而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。

下面看一下在RR隔离级别下,MVCC具体是如何操作的。

保存这两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

间隙锁

根据刚才mvvc的介绍,大家发现可重复读级别下,快照读情况下幻读是不会存在的,因为在事务开始时就保存了快照,事务中是不会读到其他事务提交的新数据的。而在当前读情况下,可能会出现幻读,所以Innodb 引擎为了解决可重复读隔离级别使用当前读而造成的幻读问题,就引出了间隙锁,举个例子,我们在事务A执行一条select * from table where id >2 and id < 5 for update语句,执行后会对table表加上 id 范围为 (2, 5) 的行加锁,此时在事务B中,我们执行insert into table values(5, ...)语句,这是判断到插入的位置(id=5)被事务 A 加了锁,于是事务 B 会生成一个插入意向锁,同时进入等待状态,直到事务 A 提交了事务。这样,通过间隙锁,当前读也避免了幻读问题。
当然,mysql在可重复读隔离级别下,虽然通过快照读(mvvc)和间隙锁很大程度避免了幻读,但是在某些极特殊情况下,还是可能会出现幻读问题的(此处不过多介绍了)。

总结

读提交和可重复读的时候有一个快照读的概念,学名叫做一致性视图,这是可重复读与不可重复读的关键;可重复读是在一个事务开始的时候生成一个当前事务的全局性快照,而读提交则是每次执行语句都重新生成一次快照;

对于一个快照来说,它能够读到那些事务版本的数据,需要新遵循以下规则:

  1. 当前事务内的更新,可以读到;

  2. 当前事务版本未提交,不能被读到;

  3. 当前版本事务A已提交,但是事务B的快照是在A提交前创建的,不能读到A的提交;(可重复读,保证读取的数据一致);

  4. 当前版本事务已提交,是在其它事务快照创建前提交的,可以被读到。

读提交和可重复读的区别就在创建快照的机制上面,读提交每执行一条语句的时候,都要重新创建一次快照,而可重复读是在事务开始时,一次创建,直到事务结束。

ps:串行化隔离级别,是读的时候加共享锁,也就是其他事务可以并发读,但不能写;写的时候加排它锁,其它事务即不能读,也不能写;


本文转载自https://www.jianshu.com/p/f692d4f8a53e

上一篇 下一篇

猜你喜欢

热点阅读