undo log主要用于事务回滚时恢复原来的数据
mysql在执行sql语句时,会将一条逻辑相反的日志保存到undo log中。因此,undo log中记录的也是逻辑日志。
当sql语句为insert时,会在undo log中记录本次插入的主键id。等事务回滚时,delete此id即可。
当sql语句为update时,会在undo log中记录修改前的数据。等事务回滚时,再执行一次update,得到原来的数据。
当sql语句为delete时,会在undo log中记录删除前的数据。等事务回滚时,insert原来的数据即可。
数据库事务四大特性中的原子性,即事务具有不可分割性,要么全部成功,要么全部失败,其底层就靠undo log实现。在某一步执行失败时,会对之前事务的语句进行回滚。
对数据库中的日志完全不熟悉的话,可以看我的另外一篇文章数据库日志——binlog、redo log、undo log扫盲
行的隐藏列
=====
在数据库中的每一行上,除了存放真实的数据以外,还存在着3个隐藏列——row_id、trx_id与roll_pointer。
row_id,行号
==========
如果当前表有整数类型的主键,则row_id就是主键的值。
如果没有整数类型的主键,则mysql会按照字段顺序选择一个非空的整数类型的唯一索引作为row_id。
如果mysql没有找到,则会自动生成一个自动增长的整数作为row_id。
那row_id和今天的MVCC有什么关系呢?
不能说一点没有吧,只能说毫无关系。
trx_id,事务号
===========
当一个事务开始执前,mysql会为这个事务分配一个全局自增的事务id。
之后该事务对当前行进行的增、删、改操作时,都会将自己的事务id记录到trx_id中。
roll_pointer,回滚指针
==================
事务对当前行进行改动时,会将旧数据写入进undo log中,再将新数据写入当前行,且当前行的roll_pointer指向刚才那个undo log,因此可以通过roll_pointer找到该行的前一个版本。
当一直有事务对该行改动时,就会一直生成undo log,最终将会形成undo log版本链。
Undo log版本链
===========
一开始,我们使用以下语句创建一个stduent表
CREATE TABLE student
(
id
INT ( 11 ) NOT NULL AUTO_INCREMENT,
name
VARCHAR ( 255 ) NOT NULL,
age
INT ( 11 ) NOT NULL,
PRIMARY KEY ( id
) USING BTREE
) ENGINE = INNODB;
现在开启第1个事务,事务id为1,执行以下插入语句。
INSERT INTO student VALUES ( 1, “a”, 24 );
那么当前行的一个示意图如下:
因为该数据是新插入的,因此它的roll_pointer指向的undo log为空。
接着开启第2个事务,分配的事务id是2,执行以下修改命令。
UPDATE student SET NAME = ‘b’ WHERE id = 1;
现在的示意图变为:
当开启第3个事务,分配到事务id是3,执行以下修改命令。
UPDATE student SET age = 25 WHERE id = 1;
示意图变为:
每一个事务对该行改动时,都会生成一个undo log,用于保存之前的版本,之后再将新版本的roll_pointer指向刚才生成的undo log。
因此roll_pointer可以将这些不同版本的undo log串联起来,形成undo log版本链。
ReadView
========
首先需要理解一下快照读与当前读
快照读:简单的select查询,即不包括 select … lock in share mode, select … for update,可能会读到数据的历史版本。
当前读:以下语句都是当前读,总是读取最新版本,会对读取的最新版本加锁。
-
select … lock in share mode
-
select … for update
-
insert
-
update
-
delete
在事务执行每一个快照读或事务初次执行快照读时,会生成一致性视图,即ReadView。
ReadView的作用是,判断undo log版本链中的哪些数据对当前事务可见。
ReadView包含以下几个重要的参数:
m_ids
======
在创建ReadView的那一刻,mysql中所有未提交的事务id集合。
min_trx_id
============
m_ids中的最小值
max_trx_id
============
mysql即将为下一个事务分配的事务id,并不是m_ids中的最大值。
creator_trx_id
================
即创建此ReadView的事务id
简要的示意图如下:
那么事务在执行快照读时,可以通过以下的规则来确定undo log版本链上的哪个版本数据可见。
如果当前undo log的版本的trx_id<min_trx_id,说明该版本对应的事务在生成ReadView之前就已经提交了,因此是可见的。
如果当前undo log的版本的trx_id≥max_trx_id,说明该版本对应的事务在生成ReadView之后才开始的,因此是不可见的。
如果当前undo log的版本的trx_id∈[min_trx_id,max_trx_id),如果在这个范围里,还要判断trx_id是否在m_ids中:
在m_ids中,说明版本对应的事务未提交,因此是不可见的。
不在m_ids中,说明版本对应的事务已经提交,因此是可见的。
如果当前undo log的版本的trx_id=creator_trxt_id,说明事务正在访问自己修改的数据,因此是可见的。
当undo log版本链表的头结点数据被判定为不可见时,则利用roll_pointer找到上一个版本,再进行判断。如果整个链表中都没有找到可见的数据,则代表当前的查询找不到数据。
MVCC在四种隔离级别下的区别
===============
在Read Uncommitted级别下,事务总是读取到最新的数据,因此根本用不到历史版本,所以MVCC不在该级别下工作。
在Serializable级别下,事务总是顺序执行。写会加写锁,读会加读锁,完全用不到MVCC,所以MVCC也不在该级别下工作。
真正和MVCC兼容的隔离级别是Read Committed(RC)与Repeatable Read(RR)
MVCC在RC与RR级别下的区别,在于生成ReadView的频率不同。
在RC级别下,当前事务总是希望读取到别的事务已经提交的数据,因此当前事务事务会在执行每一次快照读的情况下都会去生成ReadView,实时更新m_ids,及时发现那些已经提交的事务。
在RR级别下,当前事务当然也能够读取到别的事务已经提交的数据,但为了避免不可重复读,因此只会在执行第一次快照读的情况下去生成ReadView,之后的快照读会一直沿用该ReadView。
举个栗子:
在RC级别下
一开始,事务id为1的事务往表里插入了一条数据,版本链如下:
这个时候,开启事务id为2的事务,关闭自动提交模式。先执行一次select *查询,生成的ReadView如下
m_ids={2},min_trx_id=2,max_trx_id=3,creator_trx_id=2
由于该条数据的trx_id<min_trx_id,说明该版本对应的事务在生成ReadView之前就已经提交了,因此是可见的。
因此,事务2能直接查到该数据。
现在开启事务3,事务id为3,将该条数据的name改为b,并自动提交,版本链如下:
这个时候,事务2再次select *查询,由于处于RC级别下,会再次生成ReadView,此时的ReadView如下:
m_ids={2},min_trx_id=2,max_trx_id=4,creator_trx_id=2
由于最新版本的trx_id∈[2,4)且trx_id不在m_ids中,说明该版本的数据已经提交,因此是可见的,所以事务2能查到最新的数据。
而处于RR级别下:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
总结
大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。
麻烦帮忙转发一下这篇文章+关注我
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
取)**
总结
大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。
麻烦帮忙转发一下这篇文章+关注我
[外链图片转存中…(img-FTsvPjMZ-1712520016224)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!