浅析InnoDB引擎架构(已完结)

大家好,我是此林。

今天来介绍下InnoDB底层架构。

1. 磁盘架构

我们所有的数据库文件都保存在 /var/lib/mysql目录下。

由于我这边是docker部署的mysql,用如下命令查看mysql数据挂载。

docker inspect mysql-master

如下图,目前只有一个数据库 db_user,数据库本质上是文件夹的形式。

在db_user文件夹下,目前只有一张表,表空间就是idb文件形式。

 表空间idb文件以B+树的形式组织存储,其中B+树的非叶子结点存储索引段,叶子结点存储的是数据段。此外还有回滚段。段用来管理多个区。

区是表空间的单元结构,每个区大小为1M,一个区有64个连续的页。

页,是InnoDB存储引擎磁盘管理的最小单元,每个页默认大小为16K。为了保证页的连续性,Inn哦DB存储引擎每次从磁盘申请4-5个区。

行,InnoDB存储引擎数据是按行存放的。

所有文件介绍:

1. db_user:自定义的数据库

2. mysql:mysql自带数据库,包含用户、权限和管理信息表

3. performance_schema、sys:mysql自带数据库,用于mysql性能监控

4. ib_logfile0、ib_logfile1:redo_log(重做日志)

5. binlog.000001、binlog000002:binlog(二进制日志)

6. undo_001、undo_002:undo_log(回滚日志)

2. 内存架构

1. BufferPool

数据表都保存在磁盘文件中,而用户是无法直接操作磁盘文件的,必须先把数据文件加载到内存中,用户对内存中的数据进行增删改操作,最后再以一定的频率把内存中的数据刷新到磁盘中。

InnoDB内存架构中,BufferPool(缓冲区)就是类似的作用。

BufferPool 中,以页(Page)为单位,每次它会去磁盘中加载整页(16K)数据。这样做的好处是减少磁盘IO,相当于缓存的作用。

BufferPool 中的 Page有三种类型:

1. 空页(free page),空闲页,未被使用

2. clean page,被使用的页,但是数据没有被修改过

3. 脏页(dirty page),被使用的page,数据被修改过,也就是和磁盘中数据不一致的页。

2. ChangeBuffer

更改缓冲区(针对非唯一的二级索引),在执行DML语句(增删改语句)时,如果这些这些数据Page不在BufferPool中,不会区操作磁盘,而是先把数据变更存在更改缓冲区中,未来数据被读取时,再将数据合并恢复到BufferPool中,最后再刷新到磁盘。

存在的意义?

和聚簇索引不同,二级索引通常是非唯一的,并且每次以相对随机的顺序插入二级索引。同样,删除和更新可能会影响索引树中不相邻的二级索引页,如果每一次都操作一次磁盘IO,会造成大量的磁盘IO,有了ChangeBuffer后,我们可以在缓冲池中进行合并操作,减少磁盘IO。

3. Adaptive Hash Index

InnoDB不支持Hash索引,它只支持B+树索引,我们目前所有的索引,底层结构都是B+树,主要原因是Hash索引虽然快,但是只支持等值匹配,不支持范围查询。

自适应Hash索引,用于优化对BufferPool的查询。InnoDB 存储引擎会监控对表上各个索引页的查询,如果观察到Hash索引可以提升速度,则建立Hash索引。系统自动优化,无需人工干预。

查询参数:

show variables like '%hash%';

默认是开启的。

4. LogBuffer

日志缓冲区,用来保存要写入到磁盘中的log日志数据(redo log,undo log)。默认大小为16MB,日志缓冲区的日志会定期刷到磁盘中。

参数:

show variables like 'innodb_log_buffer_size';  # 缓冲区大小
show variables like 'innodb_flush_log_at_trx_commit'; # 日志刷新到磁盘的时机

innodb_flush_log_at_trx_commit:

1: 日志在每次事务提交时写入并刷新磁盘

0:每秒将日志写入并刷新磁盘一次

2:日志在每次事务提交后写入,并且每秒刷新一次到磁盘中。

3. 后台线程

1. Master Thread

核心后台线程,负责调度其他线程,还负责将缓冲区中的数据异步刷新到磁盘中,保持数据的一致性,还包括脏页的刷新、合并插入缓存、undo页的回收。

2. IO Thread

在InnoDB中大量使用AIO(异步非阻塞)来处理IO请求,极大提高数据库性能。

IO Thread主要负责这些IO请求的回调。

命令:

show engine innodb status;

3. Purge Thread

主要用于回收事务已经提交的undo_log,在事务提交之后,undo log已经无用,需要回收。

4. Page Cleaner Thread

协助Master Thread刷新脏页到磁盘的线程,减轻Master Thread的工作压力,减少阻塞。

4. InnoDB 事务原理

事务特性(ACID):

1. 原子性:事务操作不可分割,要么同时成功,要么同时失败。

2. 一致性:事务完成时,所有数据必须保证一致性。

3. 隔离性:数据库系统提供的隔离机制,保证事务在不受外部并发的影响的独立环境下运行。

4. 持久性:事务一旦 提交,对数据库的改变就是永久的。

原子性、一致性由undo_log来实现,

持久性由redo_log来实现,

隔离性由锁和MVCC机制来实现。

1. redo_log重做日志

我们知道,在执行update、delete、insert操作的时候,实际操作的是BufferPool内存中的数据,

但是BufferPool中的数据是由后台线程以一定的频率或由操作系统来决定何时同步到磁盘中的。

若BufferPool数据没来得及刷到磁盘中,服务器宕机,那么就会导致数据丢失。

redo_log出现后,流程变化如下:

1. 变更BufferPool中的数据

2. 数据页变化写入redo_log_buffer(内存)

3. 事务提交后,redo_log_buffer刷新到磁盘(redo_log)中。

4. 之后即使BufferPool没来得及刷新到磁盘,也可以通过redo_log来恢复。

问:redo_log不是多此一举吗?为什么不操作完BufferPool后,直接把脏页刷新到磁盘里?

答:

1. 我们一般在事务操作很多条记录,这些记录一般都是随机操作数据页的,此时将涉及大量的磁盘IO,性能极低。

2. 而redo_log日志文件写入都是追加顺序写入,性能高于随机磁盘IO。这种机制叫WAL(Write-Ahead-logging)——先写日志。

3. 当BufferPool中脏页的数据成功刷新到磁盘中,redo_log日志文件会被定期删除。

2. undo_log 回滚日志

回滚日志,用于记录数据被修改前的信息,作用有两个:

1. 提供回滚

2. MVCC(多版本并发控制)

可以认为当delete一条语句,undo_log中会记录一条对应的insert语句;当update一条语句,undo_log中会记录一条相反的update语句。

事务提交后,undo_log不会被立即删除,因为可能用于多版本并发控制(MVCC)。

3. MVCC 机制

1. 概要

简单来说,要保证并发安全,MYSQL觉得每次读的时候都要加锁,太损耗性能了。所以提出了MVCC机制,是不加锁的快照读(非阻塞读),提升了性能。

1. 当前读:

select ... lock in share mode (读锁)

select ... for update (写锁)

update、insert、delete

以上操作都是当前读,会对读取的记录加锁。

2. 快照读:

简单的select(不加锁)就是快照读。

Read Committed: 每次select,都生成一个快照读。

Repeatable Comitted: 开启事务后第一个select语句就是快照读的地方。

Serializable: 快照读退化成当前读。

2. 实现原理

MVCC的实现依赖数据库的行记录中的三个隐藏字段、undo_log、readView。

  • DB_TRX_ID:最近修改的事务ID,记录插入这条数据或最后一次修改该记录的事务ID。
  • DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本,用于配合undo_log,指向上一个版本。
  • DB_ROW_ID:当表中没有定义主键,MYSQL自动生成此字段作为主键。

查看命令:

idb2sdi table_name.idb

当insert的时候,产生的undo_log日志只在回滚时需要,事务提交后可以立即删除。

当update、delete时候,产生的undo_log日志不仅在回滚时需要,在快照读的时候也需要,不能立即删除。

3. 案例分析

来看下面一个案例:

undo_log版本链如下:

那我select的时候如何知道要访问哪个版本呢?

简单来说,它会拿到当前行的DB_TRX_ID(事务id),逐个和ReadView各个字段做条件判断,满足条件即返回该版本数据;若不满足会通过DB_ROLL_PTR回滚指针继续寻找版本,进行比对。

先看事务5:

假设当前事务隔离级别为RC,即每次select都会生成一个ReadView。

1. 执行第一个查询id为30的记录命令时,生成如下ReadView。读取到的事务2修改的age=10,name=A3。

ReadView
m_ids(当前活动事务id)[3,4,5]
min_trx_id(最小活动事务id,事务id时自增的)3
max_trx_id(最大活动事务id + 1,预分配的)6
creator_trx_id(当前ReadView创建的事务id)5

2. 执行第二个查询id为30的记录命令时,生成如下ReadView。读取到的是age=3,name=A30。

ReadView
m_ids(当前活动事务id)[4,5]
min_trx_id(最小活动事务id,事务id时自增的)4
max_trx_id(最大活动事务id + 1,预分配的)6
creator_trx_id(当前ReadView创建的事务id)5

但是当RR隔离级别下,第二个查询id为30的记录 会和 第一个查询id为30的记录 的结果一样,因为用的是同一个ReadView。如果这个时候不想快照读,用select for update可以查到最新的记录,因为这条加锁命令不走MVCC。

4. 总结

综上,MYSQL使用了MVCC和锁机制来保证了事务的四大特性(ACID)。

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

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

相关文章

k8s迁移——岁月云实战笔记

新系统使用rockylinux9.5,旧系统虚拟机装的是centos7 1 目标服务器 1.1 禁止swap swapoff -a vi /etc/fstab #/dev/mapper/rl-swap none swap defaults 0 0 #执行,swap一行都是0 free -h 1.2 关闭防火墙 只是为了减…

砂轮磨料基础知识及发展学习笔记

平时接触磨削的工序有很多,像平面、外圆,齿轮的齿形磨削,刀具的前刃及齿形磨削等等,花了些时间,整理了一些资料,把关于磨料的内容整理了一下。有需要的小伙伴可以耐心阅读一下。 从古代使用的简陋石头到如今…

【Spring】Spring框架之-AOP

目录 1. AOP的引入 2. AOP相关的概念 2.1 AOP概述 2.2 AOP的优势 2.3. AOP的底层原理--目前先不具体阐述,后面讲 3. Spring的AOP技术-配置文件方式 3.1 AOP相关的术语 3.2 基本准备工作 3.3 AOP配置文件方式的入门 3.4 切入点的表达式 3.5 AOP的通知类型 …

Servlet学习中遇到的一些问题及解决

错误:JavaWeb-错误:类xxx不是Servlet 解决:可能是Tomcat版本不匹配导致,更换Tomcat版本解决问题 错误:在自定义的Servlet类中不能添加 WebServlet 注解 解决:可能是WebServlet版本不匹配,更换…

tomcat的安装以及配置(基于linuxOS)

目录 安装jdk环境 yum安装 验证JDK环境 安装tomcat应用 yum安装 ​编辑 使用yum工具进行安装 配置tomcat应用 关闭防火墙和selinux 查看端口开启情况 ​编辑 访问tomcat服务 安装扩展包 重启服务 查看服务 源码安装 进入tomcat官网进行下载 查找自己要用的to…

workman服务端开发模式-应用开发-gateway长链接端工作原理

一、长链接的工作原理 Register类其实也是基于基础的Worker开发的。Gateway进程和BusinessWorker进程启动后分别向Register进程注册自己的通讯地址,Gateway进程和BusinessWorker通过Register进程得到通讯地址后,就可以建立起连接并通讯了。而Gateway进程…

Chrome 浏览器原生功能截长屏

我偶尔需要截取一些网页内容作为素材,但偶尔内容很长无法截全,需要多次截屏再拼接,过于麻烦。所以记录下这个通过浏览器原生功能截长屏的方案。 注意 这种方案并不是百分百完美,如果涉及到一些需要滚动加载的数据或者悬浮区块&am…

Kubeadm+Containerd部署k8s(v1.28.2)集群(非高可用版)

Kubeadm+Containerd部署k8s(v1.28.2)集群(非高可用版) 文章目录 Kubeadm+Containerd部署k8s(v1.28.2)集群(非高可用版)一.环境准备1.服务器准备2.环境配置3.设置主机名4.修改国内镜像源地址5.配置时间同步6.配置内核转发及网桥过滤二.容器运行时Containerd安装(所有节点)…

Nuc9 Truenas 和 Macmini4组雷电网桥 上传速度异常 1Mbp/s 解决

link: 原创文章,转载之前请标注来源博客 helsonlin 前言 咸鱼整了一台 Nuc9 组了全闪的 Nas,想着和 Macmini 组雷电网桥,但是到手弄好之后发现 iperf3 和 openspeedtest 测试上传的速度都是 1Mbps 每秒。 Google 一下发现唯一的线索就是ht…

Python从0到100(七十八):神经网络--从0开始搭建全连接网络和CNN网络

前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

Qt:QMetaObject::connectSlotsByName实现信号槽自动关联

简介 在Qt中,QMetaObject::connectSlotsByName 是一个便利的方法,它可以根据对象的对象名(objectName)自动将信号和槽连接起来。但是,要使用这个方法,必须确保: 1 控件(如按钮&…

数据挖掘之认识数据

在数据挖掘过程中,数据的认识是非常重要的一步,它为后续的数据分析、建模、特征选择等工作奠定基础。以鸢尾花数据集(Iris Dataset)数据集之鸢尾花数据集(Iris Dataset)-CSDN博客为例,下面将介绍…

useContext Hook 的使用及规范

首先 useContext 是什么? useContext 是 React 提供的一个 Hook,允许函数组件订阅 React 的 Context。 有什么用? 它使得你可以在不使用 Consumer 组件的情况下访问 Context 的值。通过 useContext,你可以轻松地在多层级组件之间…

MacOS 命令行详解使用教程

本章讲述MacOs命令行详解的使用教程,感谢大家观看。 本人博客:如烟花般绚烂却又稍纵即逝的主页 MacOs命令行前言: 在 macOS 上,Terminal(终端) 是一个功能强大的工具,它允许用户通过命令行直接与系统交互。本教程将详细介绍 macOS…

Mac上详细配置java开发环境和软件(更新中)

文章目录 概要JDK的配置JDK下载安装配置JDK环境变量文件 Idea的安装Mysql安装和配置Navicat Premium16.1安装安装Vscode安装和配置Maven配置本地仓库配置阿里云私服Idea集成Maven Cpolar快速入门 概要 这里使用的是M3型片 14.6版本的Mac 用到的资源放在网盘 链接: https://pan…

Neo4j 图数据库安装与操作指南(以mac为例)

目录 一、安装前提条件 1.1 Java环境 1.2 Homebrew(可选) 二、下载并安装Neo4j 2.1 从官方网站下载 2.1.1 访问Neo4j的官方网站 2.1.2 使用Homebrew安装 三、配置Neo4j 3.1 设置环境变量(可选) 3.2 打开配置文件(bash_profile) 3.2.1 打开终端…

【数据结构练习题】链表与LinkedList

顺序表与链表LinkedList 选择题链表面试题1. 删除链表中等于给定值 val 的所有节点。2. 反转一个单链表。3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。4. 输入一个链表,输出该链…

图书借阅管理系统|SpringBoot|HTML|web网站|Java【源码+数据库文件+包部署成功+答疑解惑问到会为止】

代码包运行启动成功!不管你有没有运行环境,哪怕你是刚买的新电脑,也包启动运行成功!有不懂的地方随便问!问到会为止! 【功能介绍】 该系统有两种角色: 管理员,读者。 1.管理员可以添…

Qt Quick:CheckBox 复选框

复选框不止选中和未选中2种状态哦,它还有1种部分选中的状态。这3种状态都是Qt自带的,如果想让复选框有部分选中这个状态,需要将三态属性(tristate)设为true。 未选中的状态值为0,部分选中是1,选…

【ue5学习笔记2】在场景放入一个物体的蓝图输入事件无效?

在场景放入一个物体的蓝图输入事件无效,那是因为你不知道gameMode这个东西这是一个用于设定游戏股则的东西, 就好比你的控制对象,你输入无效是没有指定你当前关卡中指定的控制对象是它。操作方法如下: 1.创建一个gameMode蓝图类并…