目录
个人主页:东洛的克莱斯韦克-CSDN博客
引言
多版本并发控制( MVCC )是一种用来解决 读-写冲突 的无锁并发控制 为事务分配单向增长的事务ID,为每个修改保存一个版本,版本与事务ID关联,读操作只读该事务开始 前的数据库的快照。
所以 MVCC 可以为数据库解决以下问题 在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数 据库并发读写的性能 同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题
记录的隐藏列字段
字段名称 | 大小(字节) | 描述 |
---|---|---|
DB_TRX_ID | 6 | 最近修改(修改/插入)事务ID,记录创建这条记录/最后一次修改该记录的事务ID |
DB_ROLL_PTR | 7 | 回滚指针,指向这条记录的上一个版本(即,指向undo log中的历史版本) |
DB_ROW_ID | 6 | 隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID生成一个聚簇索引 |
删除标志 | 不直接暴露 | 隐含的字段,用于标记记录是否被删除。记录被更新或删除时,并不立即从磁盘移除,而是设置删除标志,后续由purge操作物理删除 |
如下实例
create table if not exists student(
name varchar(11) not null,
age int not null
);
insert into student (name, age) values ('张三', 28);
name | age | DB_TRX_ID | DB_ROW_ID | DB_ROLL_PTR |
---|---|---|---|---|
张三 | 28 | 1 | 1 | (如果适用,可填null或具体值) |
undo 日志
在理解undo log
时,我们应该将其视为一个跨越内存和磁盘的日志系统,或者简单理解成,就是 MySQL服务进程中的一段内存缓冲区,用来保存历史数据。
版本链
InnoDB通过维护一个版本链来管理数据的多个版本。每条记录都包含一些隐藏字段,如db_trx_id
(最后操作该记录的事务ID)和db_roll_ptr
(回滚指针,指向该记录的undo日志信息)。每当事务对记录进行修改时,旧版本的数据会被保存到undo日志中,并通过db_roll_ptr
连接到版本链上。
上面的一个一个版本,我们可以称之为一个一个的快照。
Read View
Read View就是事务进行 快照读 操作的时候生产的 读视图 (Read View),在该事务执行的快照读的那一 刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被 分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)
Read View 在 MySQL 源码中,就是一个类,本质是用来进行可见性判断的。 即当我们某个事务执行快照 读的时候,对该记录创建一个 Read View 读视图,把它比作条件,用来判断当前事务能够看到哪个版本的 数据,既可能是当前最新的数据,也有可能是该行记录的 undo log 里面的某个版本的数据。
Read View类的核心数据如下
m_ids:一张列表,用来维护Read View生成时刻,系统正活跃的事务ID
up_limit_id:记录m_ids列表中事务ID最小的ID(没有写错)
low_limit_id:ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的 最大值+1(也没有写错)
creator_trx_id:创建该ReadView的事务ID
Read View通过事务ID的比较,来判断哪些快照可以被该事务看到
RR 与 RC隔离级别的本质区别
正是Read View生成时机的不同,从而造成RC,RR级别下快照读的结果的不同
在RR级别下的某个事务的对某条记录的第一次快照读会创建一个快照及Read View, 将当前系统活 跃的其他事务记录起来
此后在调用快照读的时候,还是使用的是同一个Read View,所以只要当前事务在其他事务提交更 新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,所以对之后的修改不可 见;
即RR级别下,快照读生成Read View时,Read View会记录此时所有其他活动事务的快照,这些事 务的修改对于当前事务都是不可见的。而早于Read View创建的事务所做的修改均是可见
而在RC级别下的,事务中,每次快照读都会新生成一个快照和Read View, 这就是我们在RC级别下 的事务中可以看到别的事务提交的更新的原因
总之在RC隔离级别下,是每个快照读都会生成并获取最新的Read View;而在RR隔离级别下,则是 同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。
正是RC每次快照读,都会形成Read View,所以,RC才会有不可重复读问题。