对MySQL滴MVCC理解(超详细)
学习目标
- 什么是MVCC?
- MVCC的核心概念
- MVCC 的工作原理
- MVCC 的优势
- MVCC 的劣势
- 什么是MySQL中InnoDB下滴快照读和当前读?
- 一、快照读(Snapshot Read)
- 二、当前读(Current Read)
- 三、快照读和当前读的区别
- 四、当前读、快照读和MVCC的关系
- MVCC中的RR 是如何在 RC 的基础上解决不可重复读的?
- RC, RR这两者下的 InnoDB 快照读有什么不同嘞?
- MVCC 能解决什么问题,好处是什么嘞?
- 解决的问题
- 带来的好处
什么是MVCC嘞?什么是MySQL中InnoDB下滴快照读和当前读?MVCC中的RR 是如何在 RC 的基础上解决不可重复读的?RC, RR这两者下的 InnoDB 快照读有什么不同嘞?MVCC 能解决什么问题,好处是什么嘞?
什么是MVCC?
MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种用于管理数据库并发访问的技术。它通过在数据库中存储数据的多个版本来避免读写冲突,从而提高了数据库的并发性能。MVCC 广泛应用于现代关系型数据库系统,如 PostgreSQL、MySQL(InnoDB 引擎)等。下面详细讲解 MVCC 的概念和工作原理。
MVCC的核心概念
- 数据行版本管理:
- 每一行数据在数据库中都有一个或多个版本。
- 每个版本包含创建时间戳(通常是事务ID)和删除时间戳(也可能是事务ID,或者是“未删除”标记)。
- 事务快照:
- 每个事务启动时都会创建一个“快照”,这个快照实际上是事务能看到的数据版本的集合。
- 快照确保事务只能看到在它开始之前已经提交的修改。
- 时间戳或事务ID:
- 用来标识每个数据版本的创建和删除时间。
- 时间戳或事务ID是递增的,确保唯一性。
MVCC 的工作原理
MVCC 通过在数据读写操作中使用不同的版本,实现了读写不阻塞(或者说最小化了锁的使用):
- 读取操作:
- 当一个事务读取某行数据时,它只会查看那些在它开始之前已经创建且尚未删除的版本。
- 这意味着读取操作不会阻塞写入操作,因为读取的是快照中的旧版本数据。
- 写入操作:
- 写入操作会创建一个新版本的数据行,而不会直接修改现有数据行。
- 新版本的数据行会包含一个更新的事务ID,标记为创建时间。
- 同时,旧版本的数据行不会被立即删除,而是会被标记为在某个事务ID之后删除(即在该事务提交时)。
- 删除操作:
- 删除操作类似于写入操作,它会创建一个“删除标记”的新版本,而不是直接删除旧版本。
- 这个“删除标记”实际上是一个指向该版本数据行的指针,它会在某个事务ID之后生效。
- 垃圾回收:
- 由于数据行有多个版本,数据库系统会定期进行垃圾回收,删除那些不再需要(即对所有当前活跃事务都不可见)的旧版本数据。
MVCC 的优势
- 提高并发性能:
- 读写操作可以并行进行,减少了锁的使用,从而提高了数据库的并发处理能力。
- 避免长时间锁:
- 由于读取操作不阻塞写入操作,写入操作也不需要等待读取操作完成,从而避免了长时间持有锁的情况。
- 数据一致性:
- MVCC 提供了快照隔离级别,确保每个事务看到的是一致的数据视图。
MVCC 的劣势
- 存储开销:
- 由于需要存储多个版本的数据,MVCC 会增加数据库的存储开销。
- 垃圾回收成本:
- 垃圾回收过程可能会消耗一定的计算资源,特别是在频繁更新和删除操作的数据库中。
- 复杂性:
- MVCC 增加了数据库系统的复杂性,实现和维护成本较高。
结论:
MVCC 是一种强大的并发控制机制,它通过存储数据的多个版本来实现读写不阻塞,从而提高了数据库的并发性能。虽然 MVCC 带来了额外的存储开销和复杂性,但其在现代数据库系统中的应用已经证明了其价值和实用性。
什么是MySQL中InnoDB下滴快照读和当前读?
在MySQL中,InnoDB存储引擎提供了两种主要的读操作方式:快照读(Snapshot Read)和当前读(Current Read)。这两种读操作方式在数据一致性、加锁行为以及应用场景上有所不同。下面将详细讲解这两种读操作方式。
一、快照读(Snapshot Read)
- 定义:
快照读是一种非加锁的读取方式,它读取的是事务开始时的数据快照。这意味着,在事务执行期间,无论其他事务如何修改数据,快照读始终能够看到一个一致的数据视图。 - 实现机制:
快照读依赖于InnoDB的多版本并发控制(MVCC)机制。每个事务在开始时都会创建一个Read View,用于确定在当前事务开始时已经提交的事务。在执行SELECT查询时,InnoDB会根据Read View来判断哪些数据行是可见的,从而返回符合条件的数据行。 - 应用场景:
快照读主要用于读取数据的一致性视图,确保在一个事务中多次读取同一数据时结果一致。它适用于那些不需要实时看到最新数据,但需要保证数据一致性的场景。 - 隔离级别:
- 在读提交(Read Committed, RC)隔离级别下,每次SELECT语句都会生成一个新的快照。
- 在可重复读(Repeatable Read, RR)隔离级别下(MySQL InnoDB的默认隔离级别),事务开始时生成一个快照,事务中的所有SELECT语句都会使用这个快照。
二、当前读(Current Read)
定义:
当前读是一种加锁的读取方式,它读取的是数据的最新版本,并且会对读取的数据加锁,以确保其他并发事务不能修改当前记录。
加锁行为:
当前读会对读取的数据加锁,这取决于具体的SQL语句和隔离级别。例如,SELECT … FOR UPDATE会对读取的数据行加排他锁,而SELECT … LOCK IN SHARE MODE会对读取的数据行加共享锁。
应用场景:
当前读适用于需要读取最新数据,并确保在读取期间数据不被其他事务修改的场景。它通常用于更新操作(如UPDATE、DELETE)之前的读取,以确保读取到的是最新版本的数据,并在更新时对数据行加锁以防止并发修改。
触发条件:
当前读通常由特定的SQL语句触发,如UPDATE、DELETE、INSERT等修改数据的操作,以及SELECT … FOR UPDATE和SELECT … LOCK IN SHARE MODE等显式加锁的SELECT语句。
当前读是指在读取数据时,要求读取的数据必须是最新写入的数据。这种读取方式通常伴随着一种锁机制,以确保数据的一致性。在MySQL中,当前读是通过锁定相应记录来实现的,这样其他事务在读取这些锁定的数据时将会被阻塞。当前读是一种加锁操作,也被称为悲观锁。当前读通常包括以下几种操作:
- SELECT … LOCK IN SHARE MODE:共享锁,允许其他事务读取但不允许修改。
- SELECT … FOR UPDATE:排他锁,不允许其他事务读取或修改。
- UPDATE:排他锁,用于更新数据。
- INSERT:排他锁,用于插入新数据。
- DELETE:排他锁,用于删除数据。
三、快照读和当前读的区别
- 数据一致性:
- 快照读读取的是事务开始时的数据快照,保证了事务内多次读取同一数据时结果一致。
- 当前读读取的是数据的最新版本,并加锁以确保数据的一致性和完整性。
- 加锁行为:
- 快照读是非加锁的读取方式。
- 当前读是加锁的读取方式。
- 应用场景:
- 快照读适用于需要保证数据一致性但不需要实时看到最新数据的场景。
- 当前读适用于需要读取最新数据并确保在读取期间数据不被其他事务修改的场景。
- 触发条件:
- 快照读通常由普通的SELECT语句触发。
- 当前读通常由UPDATE、DELETE、INSERT等修改数据的操作,以及显式加锁的SELECT语句触发。
四、当前读、快照读和MVCC的关系
- 当前读与MVCC:在MySQL中,当前读使用的就是MVCC机制。不过,当前读并不是直接读取数据的最新版本,而是通过加锁来确保读取到的数据是最新的。在读取数据时,如果数据被其他事务修改并提交了,那么当前读会等待其他事务提交后再读取最新数据。这实际上是通过锁机制来保证数据的一致性。
- 快照读与MVCC:快照读是基于MVCC机制实现的。在快照读中,事务读取的是数据的一个历史版本,而不是最新版本。这是通过读取undo日志中的历史版本数据来实现的。同时,read view用于判断当前事务能够看到哪个版本的数据。因此,快照读能够避免加锁,提高并发性能。
MVCC中的RR 是如何在 RC 的基础上解决不可重复读的?
MVCC(Multi-Version Concurrency Control,多版本并发控制)是数据库管理系统中一种用于提高并发性能的技术,它通过维护数据的多个版本来实现并发读取和写入操作,从而避免读锁和写锁的争用。在MVCC中,RR(Repeatable Read,可重复读)和RC(Read Committed,读已提交)是两种不同的隔离级别,它们在解决不可重复读问题上有着不同的机制。下面将详细讲解MVCC中的RR是如何在RC的基础上解决不可重复读问题的。
一、RC(读已提交)隔离级别下的不可重复读
在RC隔离级别下,每个事务的每次读取操作都会生成一个新的快照(或读视图),这意味着同一个事务内的多次读取操作可能会看到不同的数据版本。具体来说,当一个事务在执行过程中,另一个事务对数据进行了修改并提交,那么当第一个事务再次读取该数据时,它将会看到修改后的数据版本。这就导致了不可重复读问题。
二、RR(可重复读)隔离级别下的解决机制
为了解决RC隔离级别下的不可重复读问题,RR隔离级别采用了以下机制:
- 快照一致性:在RR隔离级别下,每个事务在其生命周期内只会生成一个快照(或读视图)。这意味着同一个事务内的多次读取操作都会看到这个快照中的数据版本,从而保证了数据的一致性。
- 读视图生成时机:在RR隔离级别下,快照(或读视图)是在事务开始时生成的。这意味着事务在开始时就已经确定了自己能够看到哪些数据版本,后续读取操作都会基于这个快照进行。
- 可见性判断:当事务进行读取操作时,系统会根据事务的隔离级别和读视图来判断数据的可见性。在RR隔离级别下,如果数据的事务ID小于读视图中的最小活跃事务ID(或者数据的事务ID已经提交且不在读视图的活跃事务列表中),那么该数据对当前事务是可见的。否则,数据对当前事务是不可见的,事务需要通过undo日志来读取数据的旧版本。
三、RR隔离级别下如何解决RC的不可重复读问题
通过上述机制,RR隔离级别能够在RC的基础上解决不可重复读问题:
- 避免多次生成快照:RR隔离级别避免了RC隔离级别下每次读取都生成新快照的问题,从而保证了同一个事务内的多次读取操作看到的数据版本是一致的。
- 保证数据一致性:由于RR隔离级别下每个事务只有一个快照,且快照是在事务开始时生成的,因此事务在执行过程中看到的数据版本是固定的,不会出现不可重复读的情况。
- 加强可见性判断:RR隔离级别通过严格的可见性判断机制,确保了事务只能看到符合隔离级别要求的数据版本,从而进一步增强了数据的一致性。
四、注意事项
虽然RR隔离级别能够解决不可重复读问题,但它并不能完全解决幻读问题。幻读是指在同一个事务内,两次查询得到的记录集不一致(比如第一次查询没有某条记录,第二次查询却出现了该记录)。在RR隔离级别下,虽然同一个事务内的多次查询会看到相同的数据版本,但如果其他事务在两次查询之间插入了新的记录,那么这些新记录对当前事务可能是可见的(取决于具体的实现和查询条件)。因此,在某些情况下,RR隔离级别仍然可能出现幻读问题。为了完全解决幻读问题,通常需要使用更高级别的隔离级别(如串行化)或额外的锁机制。
RC, RR这两者下的 InnoDB 快照读有什么不同嘞?
在MySQL的InnoDB存储引擎中,RC(Read Committed,读已提交)和RR(Repeatable Read,可重复读)是两种不同的事务隔离级别,它们在快照读(Snapshot Read)行为上存在一些显著的不同。以下将详细讲解这两种隔离级别下InnoDB快照读的区别。
一、快照读概述
快照读是指在事务执行期间,读取一致性的数据快照,而不受其他并发事务的修改影响。这种读取方式可以确保事务在整个执行过程中看到的数据是一致的,即使其他事务对数据进行了修改。InnoDB通过多版本并发控制(MVCC)机制来实现快照读。
二、RC隔离级别下的快照读
在RC隔离级别下,快照读的特点如下:
- 每次读取生成新快照:在RC隔离级别下,每次读取操作都会生成一个新的快照或读视图。这意味着同一个事务内的多次读取操作可能会看到不同的数据版本。
- 可见性判断:当事务进行读取操作时,系统会根据当前事务的隔离级别和读视图来判断数据的可见性。在RC隔离级别下,如果数据的事务ID小于当前事务ID且已经提交,那么该数据对当前事务是可见的。
- 可能读到最新数据:由于每次读取都会生成新快照,因此RC隔离级别下的快照读可能会读到其他事务最新提交的数据。这就导致了同一个事务内的多次读取操作可能会看到不同的结果集,即不可重复读问题。
三、RR隔离级别下的快照读
在RR隔离级别下,快照读的特点如下:
- 事务开始时生成快照:与RC隔离级别不同,在RR隔离级别下,快照或读视图是在事务开始时生成的。这意味着同一个事务内的多次读取操作都会看到这个快照中的数据版本。
- 一致性保证:由于快照是在事务开始时生成的,并且贯穿整个事务过程,因此RR隔离级别下的快照读能够保证同一个事务内的多次读取操作看到的数据版本是一致的。这就避免了不可重复读问题。
- 可见性判断与RC相同:虽然快照生成时机不同,但RR隔离级别下读取数据的可见性判断机制与RC隔离级别是相似的。都是根据数据的事务ID和当前事务的读视图来判断数据的可见性。
四、总结与对比
- 快照生成时机:RC隔离级别下每次读取都会生成新快照,而RR隔离级别下快照是在事务开始时生成的。
- 数据一致性:由于快照生成时机的不同,RR隔离级别下的快照读能够保证数据的一致性,避免不可重复读问题;而RC隔离级别下则可能出现不可重复读问题。
- 可见性判断:两者在可见性判断机制上是相似的,都是根据数据的事务ID和当前事务的读视图来判断数据的可见性。但由于快照生成时机的不同,导致在实际应用中两者读取到的数据版本可能不同。
MVCC 能解决什么问题,好处是什么嘞?
MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种数据库并发控制方法,它通过维护数据的多个版本来实现读写操作的并行执行。MVCC主要解决了以下问题,并带来了相应的好处:
解决的问题
- 读写冲突:
- 脏读:在没有MVCC的情况下,一个事务可能读取到另一个未提交事务修改过的数据,如果后者回滚,那么前者读取的就是“脏”数据。MVCC确保事务只能读取到已提交的数据版本,从而避免脏读。
- 不可重复读:在数据库操作期间,如果没有适当的隔离机制,一个事务多次读取同一数据可能会得到不同的结果,因为其他事务可能在此期间修改了这些数据。MVCC通过为每个事务提供一个一致的数据快照来解决这个问题。
- 幻读:幻读是指在同一个事务中,执行相同的查询语句,但第二次查询却返回了第一次查询中没有的新记录。MVCC可以在一定程度上减少幻读的发生,尤其是在读取时没有主动加锁的情况下。
- 数据丢失:在写写操作中,如果没有合适的并发控制机制,可能会导致数据丢失问题。MVCC通过确保每个写操作都在其自己的数据版本上进行,避免了这一问题。
带来的好处
- 提高并发性能:由于读操作不需要等待写操作完成,写操作也不会阻止其他事务进行读取,因此可以显著提高系统的并发处理能力,尤其是在读多写少的场景下。
- 减少锁的使用:虽然MVCC本身也是一种形式的锁定机制,但它减少了传统意义上的行锁或表锁的需求,从而减少了锁竞争和锁开销,提高了系统的整体性能。
- 支持多种隔离级别:MVCC能够灵活支持不同的事务隔离级别,使得开发者可以根据具体需求选择合适的隔离级别,从而在保证数据一致性的同时,提高系统的并发性能。
数据库并发场景有三种,分别为:
- 读-读:这种场景不存在任何问题,也不需要特别的并发控制。因为多个事务同时读取同一数据时,并不会对数据造成修改,所以它们之间不会产生冲突。
- 读-写:这种场景存在线程安全问题,可能会造成事务隔离性问题。具体表现为,一个事务在读取数据时,另一个事务可能正在对该数据进行修改。如果没有适当的并发控制机制,就可能导致脏读、幻读、不可重复读等问题。
- 写-写:这种场景也存在线程安全问题,可能会存在更新丢失问题。具体表现为,两个或多个事务同时尝试修改同一数据,如果没有适当的并发控制机制,就可能导致数据的最终状态不是任何一个事务所期望的结果。