目录
MVCC(多版本并发控制)是用来干什么的
丢失更新、脏读两个问题事务隔离级别都能解决,那MVCC还有什么用,直接设定合适的隔离级别不就行了?
MVCC和乐观锁的关系是什么?
MVCC的基本原理
隐藏字段
undo log 版本链
readview
MVCC概述
MVCC在RC隔离级别下和RR隔离级别下的不同表现
请读者想象一个场景,你和你的朋友同时去访问同一个mysql数据库,并且对其中的数据进行修改。这个时候,假如没有规定并发相关的控制机制,这时候往往就会出现问题。
导致的问题主要就是有两种:
MVCC主要就是为了解决类似这样的问题的。它可以通过记录数据的多个版本以及每个事务的起始时间和结束时间,来实现多个事务并发执行而不会互相干扰。
具体一点讲——在mysql中,每行数据都有一个隐藏的系统列(或者称之为隐藏字段)来存储版本号。当一条数据被修改的时候,MySQL会为这条数据生成一个新的版本,并更新该行数据的版本号。同时,mysql会记录每个事务开始的时间和结束的时间
在读取数据时,MySQL会根据事务的时间戳和数据行的版本号来确定是否可见。只有那些在事务开始之前已经提交的、新版本之前的数据才是可见的。
以一个例子来说明,假设你的朋友正在修改某个数据行,并且已经开始了一个事务。而你也想读取该数据行。根据MVCC机制,你的读操作会看到事务开始之前已经提交的版本,而不会受到你朋友正在进行的事务的干扰。
但是此时想必大家也可能会有一些疑惑,下面我将进行解答
虽然事务隔离级别可以决定事务在执行期间能否看到其他事务未提交的数据,但MVCC机制仍然是必要的,它在底层协助实现了事务隔离级别的要求,并且提供了更好的并发控制和数据一致性。
以下是MVCC机制的一些主要优点和作用:
并发性能:MVCC可以提供更高的并发性能。在MVCC中,每个事务操作的数据都有一个独立的版本,事务之间可以并发访问不同的版本,而无需进行加锁操作。这使得多个事务可以同时读取和修改不同的数据,从而提高了并发性能。
读一致性:MVCC可以实现读一致性。在可重复读隔离级别下,MVCC会为每个读操作创建一个快照,保证在事务执行期间读取到的数据始终保持一致,即使其他事务对数据进行了修改。
版本管理:MVCC可以有效地管理数据的多个版本。每次对数据的修改都会生成一个新的版本,并与事务的时间戳相关联。这样,当其他事务读取数据时,能够根据版本号和时间戳来确定可见性,避免了死锁等并发问题。
隔离级别的灵活性:MVCC机制可以提供更灵活的隔离级别。虽然事务隔离级别可以控制数据的可见性,但MVCC机制可以根据具体的需求进行定制。例如,通过设置不同的版本保留策略,可以实现更高的并发性或读取一致性。
乐观锁是一种并发控制机制,它是基于MVCC的一种实现方式。
MVCC机制通过为每个事务创建独立的数据版本来避免并发冲突。在读操作时,会根据事务的时间戳和数据版本号来确定可见性,以实现读取一致性。而乐观锁则是在写操作时使用的一种策略。
乐观锁相信事务之间不会发生冲突,它不会主动加锁,而是在提交操作时检查是否发生了冲突。具体实现方法是在读取数据时记录一个版本号或时间戳,并在提交时比较该版本号或时间戳,如果发现其他事务已经修改了相同的数据,则认为发生了冲突。
如果发生了冲突,乐观锁会回滚当前事务并重新执行,以便重新读取最新的数据,再次进行修改操作。这样可以避免了丢失更新的问题,并且减少了锁的开销,提高了并发性能。
因此,MVCC和乐观锁可以结合使用,共同实现数据库的并发控制和冲突解决。MVCC通过版本管理实现读一致性和并发性能的提升,而乐观锁则是在写操作时通过检查版本或时间戳的方式来解决并发冲突。它们共同构成了一种较为高效的并发控制策略。
MVCC的实现分为——隐藏字段、undo log版本链、readview来实现的
这里画的是一个行记录,行记录除了自己所记录的一些信息,还有一些自带的、隐藏的字段,比如trx_id(事务的id)、roll pointer(回滚指针)
trx_id 所记录的就是事务的版本号,也就是事务id
roll pointer所记录的是一个地址,其指向的是当前这条记录的另一个版本,从而形成一条链表
就是图中所示的链表,图中可以看到有许多条记录,他们都连在了一起,但是其实他们只是同一条记录的事务下的不同版本,通过隐藏字段中的roll pointer回滚指针将这些不同版本的记录连接了起来
就是由当前最新记录+该记录前的undo log日志所串成的链表
现在已经知道了一条数据有这么多的版本,那我们究竟需要返回哪个呢,由谁来决定?readview就是用来做这个的
主要是由readview+版本链访问规则/可见性算法来决定
宏观:MySQL的InnoDB存储引擎下RC(读已提交)、RR(可重复读)且快照读下基于MVCC做数据的多版本并发控制。
在进行select语句查询的时候对这种有多版本的数据返回哪一条就是由MVCC和访问规则决定的
RC:每次快照读之后生成新的readview
RR:只有第一次快照读的时候生成readview,后面都是对其的复用
这时在同事务内,RC可能两次快照读都返回的是不同版本的记录,而RR则两次快照读返回的都是相同版本
这也就解释了RC下有不可重复读的问题,RR是可重复读