深度理解mysql(二):InnnoDB存储引擎架构

一.回顾

在上一篇文正中,我简要介绍了mysql的整体架构,但是还有很多细节没有设计,比如在执行一条修改语句

update users set name = 'aa' where id =1;

时候,存储引擎会做哪些事情呢?通过上一篇的介绍,还是会走解析器,优化器,执行器,存储引擎来执行,如下如

今天我们就探索InnoDB引擎的存储架构里面的详情

二.InnnoDB存储引擎

2.1 缓冲池

数据库在修改一条数据的时候,首先肯定要先把这条数据查询出来(实际上一次会把整页的数据查询出来,会查出来多条数据,后面会讲),然后在修改,更新,储存引擎为了节省每次都要从硬盘进行IO查询,会有一个非常重要的内存组件,那就是缓冲池(Buffer Pool),如果缓冲池里面有数据了,就不需要从磁盘中查询数据了,如图所示:

update users set name = 'aa' where id =1;

对于这条语句,会先判断Id=1的数据是否在缓冲池中,如果不在就需要从磁盘中查询加载到缓冲池中,并且会对这行数据进行加锁

2.2 undo日志:数据回滚

数据库sql如果在一个事物中的时候,如果执行事物的回滚时候,就需要把更新前的数据记录在undo日志中,方便事物回滚时, 比如id=1的数据的name='bb',当我们要更改name='aa'的时候,就需要把name='bb'数据记录到undo日志中,如下图

2.3 更新缓冲池Buffer Pool的数据

当我们把要更新的那行记录从磁盘文件加载到缓冲池,同时对他加锁之后,而且还把更新前的旧值写入undo日志文件之后,我们就可以正式开始更新这行记录了,更新的时候,先是会更新缓冲池中的记录,此时这个数据就是脏数据 了。

这里所谓的更新内存缓冲池里的数据,意思就是把内存里的"id=1"这行数据的name字段修改为"aaa" 那么为什么说此时这行数据就是脏数据了呢?因为这个时候磁盘上"id=1"这行数据的name字段还是"bbb",但是内存里这行数据已经被修改了,所以就会叫他是脏数据,如下图

2.4 redo log Buffer:避免数据丢失

继续思考,刚才缓冲池中修改了数据,磁盘还未修改,这个时候如果宕机了,重启后内存中的数据丢失,就无法更新磁盘中的数据,从而导致数据丢失,这个时候,就要对内存修改写入到 redo log Buffer

注意:redo log Buffer 也是一个缓冲区,用来存放redo日志

所谓redo日志,就是记录你对数据做了什么操作,比如对"id =1 的数据,更新name=aaa"

但是这个时候,如果服务器宕机,redo日志还是没法使用的,毕竟这个时候redo日志还是仅仅在内存中的.

但是数据事务的都知道,事务要在执行commit后,才能保证数据的一致性,如果还没有执行commit时候,这个时候数据库宕机,数据都是存在内存中,这个时候数据丢失,是合理的;只有在执行commit时候,才能让数据不丢失,这个在下面会逐步讲解

2.5 redo日志写入磁盘时机:提交事务

如果这个时候,我提交一个事物,根据提交策略一定会把数据从redo log buffer刷新到 redo日志中,这个策略就是 innodb_flush_log_at_trx_commit

参数为0时候,提交时候,不会从内存刷新到磁盘中,如果此时mysql宕机,数据存在丢失

参数等于1的时候,提交事务时候,必须把数据从redo log buffer刷新到redo log日志磁盘中

参数等于2的时候,提交事务时候,不会从内存刷新到磁盘中,而是把数据写入磁盘文件对应的os cache缓存中,可能1秒后os cache才会把数据写入到磁盘文件中(如果此时宕机,会出现事务提交了,但是数据依旧丢失了) 如图:

因此当你提交事务成功后,此时做的数据的修改,一定已经刷新到磁盘中,这个时候如果mysql宕机,也会保证数据不丢失

2.6 binlog: 数据同步

上面的redo log 是一种偏向五力性质的重做日志,记录类似"对哪个数据页这中的哪条数据做了什么修改" 并且redolog 本身是InnoDB存储引擎特有的东西, bin log 是属于mysql server 级别的日志文件,并且在事物提交时候,会把redo log日志刷入磁盘,并且同时会把对应的binlog日志刷入磁盘,如下图:

binlog刷入磁盘的策略

和redo log一样,binlog这个时候也是存储在内存中,那么必然就涉及到一个何时将数据刷入磁盘中,这个时候,参考下binlog的策略,想必也会有一个参数来进行控制了,那就是 sync_binlog

这个参数有两个值

sync_binlog = 0 ,表示把数据刷入 os cache内存缓存

(默认)sync_binlog = 1 ,表示把数据刷入磁盘

2.7 事务提交

基于上面的流程,在binlog写入磁盘文件后,接着就会完成最终的事务提交,此时会把本地更新对应的binlog文件名称和这次更新的binlog日志在文件中的位置,写入到redolog日志文件中,同时在redolog日志文件里写入一个commit标记,在完成这个事情后,才算最终完成事务的提交,如图所示

2.7.1 为什么最后一步要在redo日志中写入commit标记

写入commit标记的意义是用来保持redolog日志和binlog日志的一致性;

举例说明

假设我们在提交事务事务,要执行5,6,7三个步骤后,才能算是正式提交事务,如果刚执行完步骤5,也就是redolog数据刚刷入磁盘,mysql宕机了,测试怎么办? 因为没有commit标记在redo日志中,所以判定这次事务是失败的! 如果是执行完步骤6,也就是binlog写入磁盘,这个时候mysql宕机了,怎么办? 同样道理,redolog没有commit标记,判定这次事务也是失败的! 所以必须在redolog中保留commit标记,才能判断该此次事务是成功的,而且redolog里面也记录本地更新的日志,binlog日志也有本次更新的日志,redolog和binlog数据保持一致性

2.8 内存数据通过IO线程刷新到磁盘中

update users set name = 'aa' where id =1; 通过上面的分析,当我们执行这个sql语句的时候,会把相应的日志记录并刷新到磁盘中,但是此时的内存数据还是aa,也就是说此时内存和磁盘的数据还不是一致的, mysql有一个后台的IO线程,会在之后某个时间,随机把buffer pool中的数据刷新到磁盘中,如图所示:

三.总结

通过本篇文章,就可以清晰地看到,InnoDB存储引擎主要就是包含了一些buffer pool、redo log buffer等内存里的缓存数据,同时还包含了一些undo日志文件,redo日志文件等东西,同时mysql server自己还有binlog日志文件。在你执行更新的时候,每条SQL语句,都会对应修改buffer pool里的缓存数据、写undo日志、写redo log buffer几个步骤;

但是当你提交事务的时候,一定会把redo log刷入磁盘,binlog刷入磁盘,完成redo log中的事务commit标记;最后后台的IO线程会随机的把buffer pool里的脏数据刷入磁盘里去

为什么他反而最关键的修改磁盘里的数据,要通过IO线程不定时的去执行?

为什么他不干脆直接就每次执行SQL语句,直接就更新磁盘里的数据得了?

在后面的文章中,会继续分析其中的道理 gooluck ,要睡觉了困死了😴😴😴😴

相关推荐
xieliyu.1 小时前
Java算法精讲:双指针(三)
java·开发语言·算法
love530love1 小时前
LiveTalking 数字人项目 Windows 部署完全指南(EPGF 架构)
人工智能·windows·python·架构·livetalking·epgf
星辰徐哥2 小时前
Spring Boot 微服务架构设计与实现
spring boot·后端·微服务
星辰徐哥2 小时前
Spring Boot 数据导入导出与报表生成
spring boot·后端·ui
明夜之约2 小时前
Spring Boot 自动装配源码
java·spring boot·后端
Leaton Lee2 小时前
Spring Boot分层架构详解:从Controller到Service再到Mapper的完整流程
java·spring boot·后端·架构
Micro麦可乐2 小时前
Spring Boot 实战:从零设计一个短链系统(含完整代码与数据库设计)
数据库·spring boot·后端·哈希算法·雪花算法·短链系统
Jinkxs2 小时前
Resilience4j- 与 Spring Boot 快速集成:自动配置与基础注解使用
java·spring boot·后端
毕设源码_郑学姐2 小时前
计算机毕业设计springboot网络相册设计与实现 基于Spring Boot框架的在线相册管理系统开发与应用 Spring Boot驱动的网络影集设计与实践
spring boot·后端·课程设计
辣机小司2 小时前
【踩坑记录:Spring Boot 配置文件读取值不一致?警惕 YAML 的“八进制陷阱”与 SnakeYAML 版本之谜】
java·spring boot·后端·yaml·踩坑记录