MySQL InnoDB MVCC读写逻辑分析与调测

目标

1、构建MVCC读写场景

2、gdb调试MVCC过程,输出流程图(函数级别调用过程)

前提

准备1

打开服务端

查询mysqld进程号 线程树

打开客户端,想创建几个事务号就打开几个客户端

准备2

数据库mvcc,两个表test和stu:

test表    作用:只生成事务号

create table test (id int(4))engine=innodb charset=utf8;

id

null

stu表      作用:真正触发MVCC机制的表

create table stu (id int(4),name varchar(10))engine=innodb charset=utf8;

id

name

1

zhangsan

准备3

gdb调试当前服务端的进程号

如果需要记录每次操作的输出内容,可以事先设置日志

方法:set logging file xxx.txt          set logging on

一、读写场景

1、简单两事务读写场景

Session 1

Session 2

begin;

begin;

insert into test values(1);

insert into test values(2);

select * from stu where id=1;

update stu set name=”lisi” where id=1;

select * from stu where id=1;

select * from stu where id=1;

commit;

select * from stu where id=1;

2、复杂五事务读写场景

Session 1

Session 2

Session 3

Session 4

Session 5

begin;

begin;

begin;

begin;

begin;

insert into test values(1);

insert into test values(2);

insert into test values(3);

update stu set name=”li4” where id=1;

commit;

select * from stu where id=1;

update stu set name=”wang5” where id=1;

select * from stu where id=1;

commit;

update stu set name=”zhao6” where id=1;

select * from stu where id=1;

select * from stu where id=1;

commit;

二、MVCC读写过程函数调用流程图

1、select过程

(1)、线程连接处理过程

在两个事务读写场景中,初次触发MVCC机制是select语句。它的大体函数调用流程图是这样的:

客户端与服务端建立链接的流程图:

在每次客户端输入命令后,都会进入一个handle_connection函数,这个函数中有一个循环始终在监控客户端的链接状态(即the_connection_alive的返回值),一旦客户端链接进来,就将这个函数的返回值一直置为true,循环条件成立,接下来只要客户端将客户端输入的命令解析执行(即do_co mmand函数)处理就可以了。

源码(handle_connection()函数中):

for(;;)
{
	……
	while(the_connection_alive(thd))
    {
	    if (do_command(thd))
            break;
    }
    end_connection(thd);
    ……
}
close_connection(thd, 0, false, false);
……

(2)、解析调度指令过程

收到客户端发来的命令(语句),需要对命令进行解析,这些操作都是在do_command函数中进行的。它的内部主要调用的函数有:

其中,get_command函数用来获取客户端输入的命令,然后读取命令包,对命令包进行解析,解析好了以后,就可以将命令发送出去,然后执行下一步操作。

在parse_packet函数中是一个大的switch语句,根据实测,我们找到了select * from stu where id=1;语句是执行的COM_QUERY分支。它对传进来的data进行一些参数的写入。

case COM_QUERY:
{
    data->com_query.query= reinterpret_cast<const char*>(raw_packet);
    data->com_query.length= packet_length;
    break;
}

dispatch_command函数用于执行一个连接级别的命令(COM_XXXX)。它的函数原型为bool dispatch_command(THD *thd, const COM_DATA *com_data, enum enum_server_command command);

其中这里面也有一些给传入参数进行重新赋值,在此函数中,有一个switch语句,执行到case COM_QUERY分支后,执行的主要函数为mysql_parse。

mysql_parse用于解析一条查询。mysql_execute_command执行保存在thd和lex-> sql_command中的命令。在switch语句case SQLCOM_SELECT:分支中执行execute_sqlcom_select。execute_sqlcom_select用于执行SQLCOM_SELECT情况。handle_query处理数据操作查询。

接下来进入到do_select函数。这个函数联接所有表并将其写入套接字或表中。

(3)、通过函数指针调用过程

在do_select函数之后的三个函数sub_select、join_init_read_record、rr_sequential,都是通过函数指针来调用的。根据不同的情况来确定具体的调用函数。这三个函数都是顺序执行的。它的调用流程图如下:

在do_select函数中有几行很重要的代码:

error= (*end_select)(join, 0, 0);

error= join->first_select(join,qep_tab,0);

通过查找,找到了first_select返回的是一个函数指针,进一步查看它的定义为:

typedef enum_nested_loop_state  (*Next_select_func)(JOIN *, class QEP_TAB *, bool);

通过函数指针来调用函数,实测中,第一次select过程具体调用的函数为sub_select函数。

在sub_select函数中也是通过函数指针的形式调用函数。具体的代码为:

error= (*qep_tab->read_first_record)(qep_tab);

它的定义为:

typedef int (*Setup_func)(QEP_TAB*);

它的具体调用函数为join_init_read_record。join_init_read_record用于读取记录,这个函数中最后一行,返回一个函数

return (*tab->read_record.read_record)(&tab->read_record);

通过这个函数指针,调用的具体函数是rr_sequential。由于mysql默认隔离级别是repeatable_read(RR),所以read_record具体调用的是rr_sequential函数。

(4)、进入InnoDB引擎调用过程

接下来的几个函数内部实现比较短,调用也特别简单,内部函数一般没有特别多其他函数的调用。运行到rnd_next函数就已经进入到InnoDB引擎了。它的调用流程如下:

ha_rnd_next通过随机扫描来读下一行。rnd_next在表扫描中读下一行。这个时候,就已经进入到InnoDB存储引擎了。index_first将光标放在索引的第一条记录上,并将对应的行读取到buf。index_read主要实现如何执行选择SQL查询。row_search_mvcc函数使用cusor在数据库中搜索行。这个函数主要用于共享连接的表,因此它采用的技术可以帮助重新构造事务应该看到的行。它还具有优化功能,例如预缓存行,使用AHI等

(5)、判断并选择版本过程

         到这里,才开始了mvcc机制的真正核心实现。

其中,lock_clust_rec_cons_read_sees就是判断并选择版本的地方。这个函数检查是否在一致的读取中看到一条记录。如果看到,返回值则为true;如果是检索记录的早期版本,则为false 。从函数内部可以看到具体实现:

bool lock_clust_rec_cons_read_sees(const rec_t* rec /*由innodb扫描出来的一行*/,....)
{  
	...  
	trx_id_t	trx_id = row_get_rec_trx_id(rec, index, offsets);
	return(view->changes_visible(trx_id, index->table->name));
}

在从行中获得当前事务id后,传入changes_visible函数中,通过changes_visible来判断(一致性快照视图和事务id)决定看到的行快照,即读取何种版本的行。

(6)、undo log 搜索行可见版本过程

对于一条记录,有多个版本,需要用undo log来判断它的可见性。用到的函数有row_sel_pre_vers_for_mysql、row_vers_build_for_consistent_read、trx_undo_version_build。它的调用关系为:

具体的调用流程图如下:

如果当前的一致性读视图不可见,需要通过undo log回溯, 这主要是调用了row_ver_build_for_consistent_read函数来返回可见版,row_ver_build_for_consistent_read函数中有一个for循环不断在检查版本是否可见,如果不可见则回溯找到前一个版本,直到遇到可以看见的版本。下面的源代码简略列出主要的函数实现:

for (;;)
{
    bool	purge_sees = trx_undo_prev_version_build()			
    trx_id = row_get_rec_trx_id();
    …
    // 如果当前row版本符合一致性视图,则返回
    if (view->changes_visible(trx_id, index->table->name))
    {
	    break;
	}
    …
    // 如果当前row版本不符合,则继续回溯上一个版本(回到for循环的地方)
	version = prev_version;
}

(7)、readview创建过程

通过第一次select,调试跟踪到了在创建快照的过程中调用了trx_assign_read_view,这个函数会生成当前时刻的数据库的快照。

这个函数位于trx0trx.cc文件,在源码中具体的实现为:

可以看到它会通过全局变量trx_sys中的成员,调用mvcc类的view_open方法,具体的调用关系如下:

分析:

首先需要判断一下readview的状态是否已经处于active,如果为真则open readview。进入到open函数后,判断readview是否为空,不为空则从空闲的readview链表中获取第一个readview。然后prepare,完成,退出。

实际调测中的结果:

可以看到第一次select过程通过trx_assign_read_view函数创建了一个一致性视图,并将它的地址保存在事务trx->readview。

进一步,我们开了三个事务进行update,其中两个事务已经update并且提交:

其中一个尚未提交:

此时卡在row_search_mvcc函数开始处,通过show engine innodb status\G;查看状态:

验证了此时的undo链表为6条,卡住的那条update语句还没执行完,不算。

并且,尚未提交的事务中有两次select过程,所以在mvcc中有两个readview:

2、insert\update过程

生成undo log过程

insert\update过程都是其实就是undo log生成过程的。对于rr和rc隔离级别的事务而言,当前事务不能看到其他事务已修改的数据,而是应该给它返回老版本的数据。

对于insert\update操作,生成的undo log的格式是不一样的。

undo log有两种类型:一种是insert undo log,insert操作会生成insert undo log;另一种是update undo log,update和delete操作都会生成update undo log,其中delete操作是在的删除标记置为true来分辨是delete操作。delete操作并没有立即将数据删除,这样就能够实现回滚。

insert\update操作来说,undo log生成的过程大体如下:       

关于undo log的生成逻辑详见undo log生成逻辑分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/883437.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot框架在甘肃非遗文化网站设计中的运用

3 系统分析 当用户确定开发一款程序时&#xff0c;是需要遵循下面的顺序进行工作&#xff0c;概括为&#xff1a;系统分析–>系统设计–>系统开发–>系统测试&#xff0c;无论这个过程是否有变更或者迭代&#xff0c;都是按照这样的顺序开展工作的。系统分析就是分析系…

数据库——sql语言学习 查找语句

一、什么是sql SQL是结构化查询语言&#xff08;Structured Query Language&#xff09;的缩写&#xff0c;它是一种专门为数据库设计的操作命令集&#xff0c;用于管理关系数据库管理系统&#xff08;RDBMS&#xff09;。 二、查找相关语句 ‌‌首先&#xff0c;我们已经设…

【洛谷】P10417 [蓝桥杯 2023 国 A] 第 K 小的和 的题解

【洛谷】P10417 [蓝桥杯 2023 国 A] 第 K 小的和 的题解 题目传送门 题解 CSP-S1 补全程序&#xff0c;致敬全 A 的答案&#xff0c;和神奇的预言家。 写一下这篇的题解说不定能加 CSP 2024 的 RP 首先看到 k k k 这么大的一个常数&#xff0c;就想到了二分。然后写一个判…

Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】

Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】 目录 Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】 一、简单介绍 二、单例模式 (Singleton Pattern) 1、什么时候使用单例模式 2、单例模式的好处 3、使用单例模式的…

sheng的学习笔记-logback

基础知识 什么是logback Logback是一个用于Java应用程序的日志框架&#xff0c;提供了更好的性能、可扩展性和灵活性。 与Log4j相比&#xff0c;Logback提供了更快的速度和更低的内存占用&#xff0c;这使得它成为大型企业级应用程序的理想选择。 ‌logback和slf4j的关系是…

Hadoop安装与配置

一、Hadoop安装与配置 1、解压Hadoop安装包 找到hadoop-2.6.0.tar.gz,将其复到master0节点的”/home/csu”目录内&#xff0c;解压hadoop [csumaster0 ~]$ tar -zxvf ~/hadoop-2.6.0.tar.gz 解压成成功后自动在csu目录下创建hadoop-2.6.0子目录&#xff0c;可以用cd hadoo…

WGS1984快速度确定平面坐标系UTM分带(快速套表、公式计算、软件范围判定)

之前我们介绍了坐标系3带6带快速确定带号及中央经线&#xff08;快速套表、公式计算、软件范围判定&#xff09;就&#xff0c;讲的是CGCS2000 高斯克吕格的投影坐标系。 那还有我们经常用的WGS1984的平面坐标系一般用什么投影呢? 对于全球全国的比如在线地图使用&#xff1a…

9.sklearn-K-means算法

文章目录 环境配置&#xff08;必看&#xff09;头文件引用K-means算法1.简介2.API3.代码工程4.运行结果5.模型评估6.小结优缺点 环境配置&#xff08;必看&#xff09; Anaconda-创建虚拟环境的手把手教程相关环境配置看此篇文章&#xff0c;本专栏深度学习相关的版本和配置&…

前端sm2国密加密时注意

如下方法&#xff1a; export function encrypt(str) {const sm2 require("sm-crypto").sm2;const cipherMode 1; // 1 - C1C3C2&#xff0c;0 - C1C2C3&#xff0c;默认为1//自定义密钥let publicKey "xxxxxxxx";//此处加密let a sm2.doEncrypt(str,…

django项目添加测试数据的三种方式

文章目录 自定义终端命令Faker添加模拟数据基于终端脚本来完成数据的添加编写python脚本编写shell脚本执行脚本需要权限使用shell命令来完成测试数据的添加 添加测试数据在工作中一共有三种方式&#xff1a; 可以根据django的manage.py指令进行[自定义终端命令]可以采用第三方…

数据集-目标检测系列-兔子检测数据集 rabbit >> DataBall

数据集-目标检测系列-兔子检测数据集 rabbit >> DataBall 数据集-目标检测系列-兔子检测数据集 rabbit 数据量&#xff1a;8k 想要进一步了解&#xff0c;请联系。 DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加…

如何在Excel中快速找出前 N 名,后 N 名

有如下销售额统计表&#xff1a; 找出销售额排前 10 名的产品及其销售额&#xff0c;和销售额排倒数 10 名以内的产品及其销售额&#xff0c;结果如下所示&#xff1a; 前 10 名&#xff1a; spl("E(?1).sort(ProductSales:-1).to(10)",A1:C78)后 10 名&#xff1…

当大语言模型应用到教育领域时会有什么火花出现?

当大语言模型应用到教育领域时会有什么火花出现&#xff1f; LLM Education会出现哪些机遇与挑战? 今天笔者分享一篇来自New York University大学的研究论文&#xff0c;另外一篇则是来自Michigan State University与浙江师范大学的研究论文&#xff0c;希望对这个话题感兴趣…

顶点缓存对象(VBO)与顶点数组对象(VAO)

我们的顶点数组在CPU端的内存里是以数组的形式存在,想要GPU去绘制三角形,那么需要将这些数据传输给GPU。那这些数据在显存端是怎么存储的呢?VBO上场了,它代表GPU上的一段存储空间对象,表现为一个unsigned int类型的变量,GPU端内存对象的一个ID编号、地址、大小。一个VBO对…

Spring:项目中的统一异常处理和自定义异常

介绍异常的处理方式。在项目中&#xff0c;都会进行自定义异常&#xff0c;并且都是需要配合统一结果返回进行使用。 1.背景引入 &#xff08;1&#xff09;背景介绍 为什么要处理异常&#xff1f;如果不处理项目中的异常信息&#xff0c;前端访问我们后端就是显示访问失败的…

c# 子类继承父类接口问题

在C#中&#xff0c;子类并不直接“继承”父类继承的接口&#xff0c;但子类的确会继承父类对接口的实现&#xff08;如果父类实现了该接口&#xff09;。这里有一些关键的概念需要澄清&#xff1a; 接口继承&#xff1a;当一个类实现了某个接口时&#xff0c;它必须实现接口中…

新峰商城之订单(一):确认页面开发

新峰商城订单从生成到处理结束&#xff0c;主要以下几个流程&#xff1a; &#xff08;1&#xff09;提交订单&#xff08;商城用户发起&#xff09; &#xff08;2&#xff09;订单入库&#xff08;后台逻辑&#xff09; &#xff08;3&#xff09;支付订单&#xff08;商城…

化繁为简:中介者模式如何管理复杂对象交互

化繁为简&#xff1a;中介者模式如何管理复杂对象交互 中介者模式 是一种行为型设计模式&#xff0c;定义了一个中介者对象&#xff0c;来封装一组对象之间的交互。中介者模式通过将对象之间的交互行为从多个对象中抽离出来&#xff0c;集中封装在一个中介者对象中&#xff0c;…

【开源】 mRemoteNG 一键搞定!推荐一款强大的.NET多协议远程连接管理器

今天给大家推荐一款.NET开发的多协议、选项卡式远程连接管理器mRemoteNG。 mRemoteNG 是 mRemote 的一个分支&#xff1a;一个开源的、标签式的、多协议的、用于 Windows 的远程连接管理器。 mRemoteNG是一个开源的Windows远程连接管理器&#xff0c;它支持多种协议&#xff0c…

基于小安派AiPi-Eyes-Rx的N合1触摸屏游戏

基于小安派AiPi-Eyes-Rx的N合1触摸屏游戏 目前存在的游戏&#xff1a; 植物大战僵尸&#xff1a;demos/pvz羊了个羊&#xff1a;demos/yang消消乐&#xff1a;demos/xiaoxiaole华容道&#xff1a;demos/huarongdao PVZ功能展示可见&#xff1a; 羊了个羊&#xff1a; 消消…