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生成逻辑分析。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

韩媒专访CertiK首席商务官:持续关注韩国市场,致力于解决Web3安全及合规问题

作为Web3.0头部安全公司&#xff0c;CertiK在KBW期间联合CertiK Ventures举办的活动引起了业界的广泛关注。CertiK一直以来与韩国地方政府保持着紧密合作关系&#xff0c;在合规领域提供强有力的支持。而近期重磅升级的CertiK Ventures可以更好地支持韩国本地的区块链项目。上述…

Nmap网络扫描器基础功能介绍

怎么快速知道网络中存在哪些设备呢&#xff1f;我们可以借用扫描工具Nmap来实现这个功能。 下载 Windows系统可以前往Nmap官网下载安装包。 Linux使用对应的包管理器可以直接安装&#xff0c;命令如下 # Debian/Ubuntu apt install nmap# RedHat/Fedora yum install nmap …

数据在内存中的存储(下)

目录 前言一、浮点数在内存中的存储1.1 练习1.2 浮点数的存储1.2.1 浮点数存的过程1.2.2 浮点数取的过程 1.3 题目解析 总结 前言 前面一期我们主要说到整形在数据中的存储方式&#xff0c;这期我们来看看浮点数在内存中是如何存储的&#xff0c;话不多说&#xff0c;正文开始…

运行python程序

1 终端运行 1.1、直接在python解释器中书写代码 >>> print(法外狂徒) 法外狂徒 …

【数据结构初阶】排序算法(上)插入排序与选择排序

文章目录 1.排序概念及运用1. 1 概念1. 2 运用1.3 常见排序算法 2. 插入排序2. 1 直接插入排序2. 2 希尔排序2. 2. 1 希尔排序的时间复杂度 3. 选择排序3. 1 直接选择排序3. 2 堆排序3. 3 Top-K问题 1.排序概念及运用 1. 1 概念 排序&#xff1a;所谓排序&#xff0c;就是使一…

PySimpleGUI:简化 Python 中的 GUI 开发

作为一个算法工程师&#xff0c;避免不了需要标注数据&#xff08;当然还有其他需求&#xff09;&#xff0c;标注数据时还是需要一个很好的工具&#xff0c;此时需要你来写一个图形用户界面&#xff08;GUI&#xff09;&#xff0c;太难了&#xff5e; 然而&#xff0c;PySim…

Java语言程序设计基础篇_编程练习题**18.34 (游戏:八皇后问题)

目录 题目&#xff1a;**18.34 (游戏:八皇后问题) 代码示例 代码解析 输出结果 使用文件 题目&#xff1a;**18.34 (游戏:八皇后问题) 八皇后问题是要找到一个解决方案&#xff0c;将一个皇后棋子放到棋盘上的每行中&#xff0c;并且两个皇后棋子之间不能相互攻击。编写个…

B-树(不是B减树)原理剖析(1)

目录 B树的主要特性&#xff1a; B树的操作&#xff1a; B树的优点&#xff1a; 为什么要发明出B-树&#xff1f; B树的概念和原理剖析 原理图讲解(部分讲解在图中) 初始化结点&#xff1a; 处理数据数量计算(了解) 底层代码实现(加深理解) 前些日子我们学了AVl树&…

MySQL InnoDB undo log生成逻辑分析

引用《InnoDB存储引擎》中有一句话&#xff0c;特别重要&#xff1a; 用户通常对undo有这样的误解&#xff1a;undo用于将数据库物理地恢复到执行语句或事务之前的样子---但事实并非如此。 undo是逻辑日志&#xff0c;因此只是将数据库逻辑地恢复到原来的样子。所有的修改都被…

通信工程学习:什么是NFV网络功能虚拟化

NFV&#xff1a;网络功能虚拟化 NFV&#xff08;Network Function Virtualization&#xff09;&#xff0c;即网络功能虚拟化&#xff0c;是一种通过虚拟化技术实现网络功能的技术手段。它借鉴了x86服务器的架构&#xff0c;将传统的网络硬件设备如路由器、交换机、防火墙、负载…

neo4j:ubuntu环境下的安装与使用

一、neo4j安装 1. 下载安装包 进入网站&#xff1a;https://neo4j.com/deployment-center/#community 在上图中选择下载即可&#xff08;社区版免费&#xff09; 注意&#xff1a;neo4j的版本要和电脑安装的jdk版本对应&#xff0c;jdk版本使用java --version查看&#xff1a;…

华为认证HCIA篇--网络通信基础

大家好呀&#xff01;我是reload。今天来带大家学习一下华为认证ia篇的网络通信基础部分&#xff0c;偏重一些基础的认识和概念性的东西。如果对网络通信熟悉的小伙伴可以选择跳过&#xff0c;如果是新手或小白的话建议还是看一看&#xff0c;先有个印象&#xff0c;好为后续的…

复制他人 CSDN 文章到自己的博客

文章目录 0.前言步骤 0.前言 在复制别人文章发布时&#xff0c;记得表明转载哦 步骤 在需要复制的csdn 文章页面&#xff0c;打开浏览器开发者工具&#xff08;F12&#xff09;Ctrl F 查找"article_content"标签头 右键“Copy”->“Copy element”新建一个 tx…

【Godot4自学手册】第四十八节创建雨粒子效果

今天我们要利用GPU粒子节点玩雨粒子效果&#xff0c;下雨天。 一、添加GPU粒子系统 添加GPUParticles2D节点。选择根节点&#xff0c;单击添加按钮&#xff0c;选择GPUParticles2D&#xff0c;完成添加。 二、修改属性 1.设置粒子数量。 在GPUParticles2D检查器中将Amount设…

速记篇 |TCP/IP五层模型怎么背,OSI七层模型怎么背?

背景 记忆TCP/IP五层模型和OSI七层模型可以通过理解每一层的功能、作用以及它们之间的逻辑关系来进行。下面分别给出这两个模型的记忆方法和要点&#xff1a; TCP/IP五层模型 TCP/IP五层模型是一个简化的模型&#xff0c;从下到上依次为&#xff1a; 1.物理层&#xff08;Physi…

计算机毕业设计之:云中e百货微信小程序设计与实现(源码+文档+定制)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

微信小程序转化为uni-app项目

前言&#xff1a; 之前自己做一个uni-app的项目的时候前端需要实现一个比较复杂的动态tab和swiper切换的功能&#xff0c;但是由于自己前端抠脚的原因没有写出来&#xff0c;然后自己在网上搜索的时候发现了有个微信小程序里面的页面及极其的符合我的需求。那么问题来了我该如何…

『功能项目』QFrameWork拾取道具UGUI【69】

本章项目成果展示 我们打开上一篇68QFrameWork扔到地上UGUI的项目&#xff0c; 本章要做的事情是实现当物品在地上时&#xff0c;点击物品将对应物品转移到道具栏中 制作一个提示UI界面 添加Button组件设置为点击即将父物体隐藏 拖拽到文件夹中在场景中删除 创建脚本&#xf…

springboot实战学习(9)(配置mybatis“驼峰命名“和“下划线命名“自动转换)(postman接口测试统一添加请求头)(获取用户详细信息接口)

接着学习。之前的博客的进度&#xff1a;完成用户模块的注册接口的开发以及注册时的参数合法性校验、也基本完成用户模块的登录接口的主逻辑的基础上、JWT令牌"的组成与使用以及完成了"登录认证"&#xff08;生成与验证JWT令牌&#xff09;具体往回看了解的链接…

python虚拟环境创建使用

环境变量中配置 vi /etc/profile 注意安装完python环境之后要添加以下代码&#xff0c;配置虚拟环境的命令才能正确使用&#xff1a; PATH$PATH:/usr/local/python3 PATH$PATH:/usr/local/python3/bin 创建&#xff1a;virtualenv venv 激活虚拟环境&#xff1a;source ./v…