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

news2024/9/30 13:58:26

目标

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.coloradmin.cn/o/2166501.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM(HotSpot):虚拟机栈(JVM Stacks)之线程问题排查方法

文章目录 前言一、CPU占用过大二、程序运行很长时间没有结果三、总结 前言 本篇讲的排查都是基于Linux环境的。 一、CPU占用过大 这个一般是出现了死循环导致的。 1、先用top命令查看占用CPU的进程ID top2、再用ps命令查看对应的线程 就看一查看到对应的线程id ps H -eo …

王道考研视频——操作系统笔记

操作系统第一章&#xff01;入门 王道考研视频——操作系统笔记&#xff0c;第一部分&#xff0c;操作系统的概念和体系结构 0.0 课程白嫖指南_哔哩哔哩_bilibili0.0 课程白嫖指南是王道计算机考研 操作系统的第1集视频&#xff0c;该合集共计84集&#xff0c;视频收藏或关注UP…

openkylin介绍及其特点和优势

openKylin&#xff08;开放麒麟&#xff09;openKylin&#xff08;开放麒麟&#xff09; 社区是在开源、自愿、平等和协作的基础上&#xff0c;由基础软硬件企业、非营利性组织、社团组织、高等院校、科研机构和个人开发者共同创立的一个开源社区&#xff0c;致力于通过开源、开…

51单片机应用开发---keil 创建一个新工程并用Protues 8仿真(以点亮LED为例)

实现目标 1、掌握keil V5软件 创建一个新工程&#xff1b; 2、具体目标&#xff1a;1.会新建一个工程&#xff1b;2.编程实现点亮开发板的LED1. 一、新建工程步骤 1.1 在桌面上新建一个名字为 LED的文件夹 1.2 双击打开Keil uVision5 软件&#xff0c;点击 Project —>…

HTML和CSS中的浮动以及边框塌陷解决方案(内置练习及答案)

一、浮动概述 在HTML和CSS中&#xff0c;“浮动”&#xff08;Float&#xff09;是一种布局技术&#xff0c;它允许元素脱离其正常的文档流&#xff0c;向左或向右移动&#xff0c;直到它的外边缘碰到包含框或另一个浮动元素的边缘。浮动元素仍然保持块级盒模型的特性&#xff…

【GUI设计】基于图像分割的GUI系统(6),matlab实现

博主简介&#xff1a; 如需获取设计的完整源代码或者有matlab图像代码项目需求/合作&#xff0c;可联系主页个人简介提供的联系方式或者文末的二维码。博客内容有疑问可联系沟通&#xff08;博主邮箱&#xff1a;3249726188qq.com&#xff09;。 ~~~~~~~~~~~~~~~~~~~~~~~…

【ComfyUI】减少连线的节点——cg-use-everywhere

源码&#xff1a;https://github.com/chrisgoringe/cg-use-everywhere 介绍&#xff1a;一组节点&#xff0c;允许数据“广播”到一些或所有未连接的输入端。大大减少了链接混乱。 【秋葉aaaki】comfyui一键运行包 夸克网盘&#xff1a;https://pan.quark.cn/s/64b808baa960 …

PMP--二模--解题--131-140

文章目录 反例14.敏捷–角色–教练/项目经理–仆人式领导–职责–核心工作–消除障碍&#xff1b;–作用–促进合作&#xff08;关键字&#xff1a;合作&#xff0c;一起解决问题&#xff09;--不是出现“合作”就一定选131、 [单选] 一个项目经理被分配到一个针对客户的分析项…

【网络安全】网络基础第一阶段——第三节:网络协议基础---- VLAN、Trunk与三层交换技术

目录 一、交换机 1.1 交换机定义 1.1.1 交换机 1.2 工作原理 1.2.1 数据帧的转发 1.2.2 交换机处理数据帧的三种行为 1.2.3 交换机通信 二、虚拟局域网&#xff08;VLAN&#xff09; 2.1 虚拟局域网简介 2.1.1 为什么需要VLAN 2.1.2 广播域的分割与VLAN的必要性 2.…

【Linux】权限管理——专治不会设置权限而头疼

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;Linux 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、Linux权限管理1.1 Linux用户类型1.2 文件类型和访问权限1.3 文件访问权限的相关设置方法1.3.1 更改文…

vue2加载本地html文件

实现方法&#xff1a; 1、在public文件夹下创建static文件夹&#xff0c;static文件夹下放置需要展示的html文件&#xff0c;有没有static都没问题&#xff0c;需要在public文件夹下 <div style"width: 100%"><!--静态html资源--><div class"t…

C语言⾃定义类型:结构体

目录 前言 1. 结构体类型的声明 1.1 结构的声明 1.2 结构体变量的创建和初始化 1.3 结构的特殊声明 1.4 结构的⾃引⽤ 2.结构体内存对齐 2.1 对⻬规则 实例讲解 2.2 为什么存在内存对⻬&#xff1f; 2.3 修改默认对⻬数 3. 结构体传参 4. 结构体实现位段 4.1 什么…

H7-TOOL脱机烧录增加国产3PEAK混合信号单片机TPS32和富芮坤FR8008,FR8003和FR2012支持

支持单路&#xff0c;1拖4和1拖16脱机烧录 3PEAKS思瑞浦混合信号单片机TPS32M支持 1、在线烧录&#xff0c;脱机烧录均正常。 2、注意事项&#xff1a;推荐使用整片擦除。 在线烧录&#xff1a; 脱机烧录&#xff1a; 接线效果&#xff1a; 富芮坤FR8008x支持&#xff0c;支…

3-2 RTE对Runnable的作用

返回总目录->返回总目录<- 一、前言 通过RTE给runnable提供触发事件。 runnable是可以被触发的,但是需要通过RTE来实现这个触发和调用runnable通过RTE给runnable提供所需资源。 RTE将runnable需要的一些资源通过接口传输给它(Port的实现)将BSW和SWC做隔绝。 因此OS和r…

基于标签相关性的多标签学习

本文所涉及所有资源均在 传知代码平台可获取。 目录 论文概述 什么是多标签学习 论文贡献 算法流程 挖掘“主题“——提取标签相关性 训练 &#x1d440; &#x1d447; M T 模型——拟合{特征集, 主题集合} 用标记相关性扩增数据集 再次训练拟合 &#x1d440; M模型——对真…

[Unity Demo]从零开始制作空洞骑士Hollow Knight第十一集:制作法术系统的回血机制和火球机制

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、制作法术系统的回血机制 1.制作动画以及使用UNITY编辑器编辑2.使用代码和PlaymakerFSM制作回血机制二、制作法术系统的火球机制 1.制作动画以及使用UNITY编…

LiveNVR监控流媒体Onvif/RTSP功能-支持电子放大拉框放大直播视频拉框放大录像视频流拉框放大电子放大

LiveNVR监控流媒体Onvif/RTSP功能-支持电子放大拉框放大直播视频拉框放大录像视频流拉框放大电子放大 1、视频广场2、录像回看3、RTSP/HLS/FLV/RTMP拉流Onvif流媒体服务 1、视频广场 视频广场 -》播放 &#xff0c;左键单击可以拉取矩形框&#xff0c;放大选中的范围&#xff…

23小时续航创造历史!华硕灵耀14 Air+酷睿Ultra 200V系列处理器首发评测

一、前言&#xff1a;能效为王的酷睿Ultra 200V系列处理器 ARM平台的功耗远低于X86平台&#xff0c;ARM处理器笔记本(比如MacBook Air)的续航远高于X86&#xff0b;Windows笔记本&#xff01; 在我们看来&#xff0c;这些似乎就是理所当然、天经地义的事情&#xff0c;毕竟几十…

Git 撤销一个已经push到远端仓库的commit

在 Git 中&#xff0c;撤销一个已经推送到远程仓库的改动有几种不同的方法&#xff0c;具体取决于你是否想要完全删除改动&#xff0c;还是只是恢复文件的某个状态。以下是常见的几种方法&#xff1a; git revert 撤销特定的commit git revert 是最安全的方法&#xff0c;因为…

【MySQL 05】表的约束

目录 1.约束的概念 2 空属性约束 3.默认值约束 4. 列描述&#xff08;comment&#xff09; 5.补零&#xff08;zerofill&#xff09; 6.主键约束 &#xff08;primary key&#xff09; 基本案例 主键的删除与添加 复合主键 复合主键的删除与添加 7. 自增长约束&#xf…