mysql四种事务隔离级别以及mvcc并发访问控制

Mysql是一个高性能支持并发访问的开源数据库,支持多种事务隔离级别以解决脏读,幻读等现象。分别介绍如下:

1.未提交读:

         在该级别下,即使事务中的修改没有被提交,其他事务仍然可以访问到这条被修改的记录,也就是说,不同事务之间的修改是相互可见的,这也就称为脏读。什么是脏读,脏读就是无效数据的读取。比如事务T1将值a修改为2,此时事务T2读取到a值为2,此时如果T1撤销了对a的修改,这就导致T2读取到的数据是无效的。一般情况下不会使用该隔离级别。

2.提交读:

         在该级别下,当前事务只能看见已经提交的事务所做的修改。也就是说,一个事务如果不提交的话,所做的修改对其他事务是不可见的。该事物隔离级别是大多是数据库的默认隔离级别,但实际上在该级别下,还会产生不可重复读的问题。比如事务T1读取b值为2,这时事务T2将b值更新为3,然后提交,这个时候,如果事务T1再次读取b得到的结果就是3。也就是说在同一个事务中,执行先后两次同样的查询,得到不一样的结果,这就是不可重复读。

3.可重复读:

        该级别在提交读的基础上,解决了不可重复读的问题。但该级别还是不能解决幻读的问题。该级别是mysql的默认隔离级别。要具体了解该级别的工作原理以及如何解决幻读就必须熟悉MVCC。

4.串行化读:

        该级别会强制事务串行执行,虽然避免了幻读问题,但并发量大大下降,实际用处不多。

MVCC即muti-version-concurrency-control(多版本并发控制)。InnoDB是一个多版本的存储引擎,它不仅存储当前的数据,也存储当前数据修改前的老版本数据来支持事务并发访问和事务回滚。为了进行版本标识,InnoDB实际上为存储的每一行记录都添加了三个字段,分别如下:

1.DB_TRX_ID(6字节)。该列保存了对该条记录进行插入或者更新的最后一个事务的唯一标识(删除操作也被认为是更新,而且会在被该行的一个特殊点标识为删除)。该标识实际上是一个事务的系统版本号而且是单调递增的。

2.DB_ROLL_PTR(7字节)。该字段也叫回滚指针,指向的是一条写在rollback segment中的undo log(事务中的sql每做一次更新,都会将源数据copy一份放在undo log中,undo log又分为insert undo log 和update undo log)记录。

3.DB_ROW_ID(6字节)。每插入一行数据就单调递增,相当于记录在InnoDB中的行号。

实现MVCC的关键就是DB_TRX_ID和两个字段。现在假设我们有如下事务:


      MVCC的关键就体现在A步骤上。在这里,我们创建了readview(具体概念直接查找Mysql官方使用手册),也就是当前事务的所有读(不完全是)操作都是基于这步readview创建的时候的数据库的'快照'。在这里,因为readview创建前,transcation1已经提交,那么transcation1中修改的数据对当前事务就是可见的;transcation 2插入一条记录,这时innoDB会生成一条undo log用来复制更新这条记录之前的数据。此时执行A查询时,虽然数据库中实际存储的是(2,1),但实际上InnoDB会根据number=1这条记录DB_TRX_ID字段找到对应的undo log记录,然后把undo log存储的之前的(1,1)版本的数据返回。因为读取的是历史版本数据,因此InnoDB并不会进行加锁处理,这也就是无锁一致性读。

      通过上面的分析我们可知,其实,我们通过常规select读取到的数据其实都是之前版本的历史数据,与此对应,自然有可以读取到当前数据库中的数据的方法,这里引出当前读和一致性读(快照读)。

一致性读: 也叫快照读,读的是历史数据(读取的数据都基于readview创建时已经在数据库中并且相应事务已经提交的),这也是mvcc机制下,mysql支持高并发访问的关键。一致性读只针对常规select语句,像update,delete,insert语句都是基于当前数据的。

当前读:顾名思义,读取的是当前的数据库中实际存储的数据,要实现当前读,有两种方法:select *** lock in share mode,   select *** for update;使用这种形式的语句能读到当前数据,但会引入加锁的问题(IS,IX锁)。

下一篇博客会详细介绍下mysql下的各种锁机制。



猜你喜欢

转载自blog.csdn.net/summermangozz/article/details/78172380