mvcc,多版本并发控制(Multi-Version Concurrency Control),是一种用于数据库管理系统中的并发控制方法.
在传统的并发控制方法中,如锁定机制,当一个事务修改数据时,会对相关的数据对象进行锁定,其他事务需要等待该锁释放才能进行操作。这种方法存在着事务之间相互等待的问题,容易导致死锁和性能瓶颈。
mvcc通过在数据库中保留数据的多个版本来解决这个问题。每次事务读取数据时,会根据该事物的时间戳或版本号获取对应的数据版本。当一个事务修改数据的时候,会创建一个新的数据版本,并将该事物的时间戳或版本号与数据版本关联起来。这样,在并发执行过程中,不同事务读取的是不同的数据版本,从而避免了锁的竞争和冲突,提高了并发性能。维护一个数据的多个版本,使得读写操作没有冲突,它的底层实现主要分为了三个部分,第一个是隐藏字段,第二个是undo log日志,第三个是readView读视图。
隐藏字段是指:在mysql中给每个表都设置了隐藏字段,有一个是trx_id(事务id),记录每一次操作的事务id,记录每一次操作的事务id(自增).另一个字段是roll_pointer(回滚指针),指向上一个版本的事务版本记录地址。(row_id隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段)
undo log主要作用是记录回滚日志,存储老版本数据,在内部会形成一个版本链,在多个事务并行操作某一行记录,记录不同事务修改数据的版本,通过roll_pointer指针形成一个链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。sql语句中insert、update、delete的时候都会产生便于数据回滚的日志,在insert的时候,产生的undo log 日志只在回滚时需要,在事务提交后,可被立即删除。而update、delete的时候,产生undolog日志不仅在回滚时需要,mvcc版本访问也需要,不会立即被删除。
readView解决的是一个事务查询选择版本的问题,在内部定义了一些匹配规则和当前的一些事务id判断该访问哪个版本的数据,不同的隔离级别快照读是不一样的,最终访问的结果不一样.
在 RC(Read Committed)隔离级别下,每一次执行快照读都会生成一个新的 ReadView。这意味着每个快照读都会基于当前数据库的提交状态生成一个新的 ReadView,并且在读取数据时只能看到已经提交的数据。因此,对于同一事务的多次快照读,可能会在不同的时间点看到不同的数据快照。
四个字段按照版本比较规则,数据链从上到下依次进行是否符合条件的比对。
简单理解:可以看readView之前数据链版本哪个已提交,所以第一次查询id为30的记录,可查询到的版本为事务2历史版本。第二次查询id为30的记录,可查询版本为事务3历史版本。
而在 RR(Repeatable Read)隔离级别下,仅在事务中第一次执行快照读时会生成一个 ReadView,并且后续的快照读都会复用该 ReadView。这意味着在同一事务中的多次快照读将看到相同的数据快照,即使其他事务已经对数据进行了修改或提交。
因此,RC 隔离级别下的快照读会在每次执行时生成新的 ReadView,而 RR 隔离级别下的快照读则会在事务中第一次执行时生成 ReadView 并在后续复用,这两种隔离级别下的快照读行为差异会导致在多版本并发控制中访问的数据结果不同。
这里RC与RR都是快照读,读取的是记录数据的可见版本,可能是历史记录,不加锁,是非阻塞读。ReadView是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的未提交事务的id。与之相对的概念是当前读,读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前事务,会对读取的记录进行加锁。sql语句中update、insert、delete都相当于上了排它锁,都是一种当前读。