likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 2

一.回顾

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

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

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

深度理解mysql(二):InnnoDB存储引擎架构 今天我们就探索InnoDB引擎的存储架构里面的详情

二.InnnoDB存储引擎

2.1 缓冲池

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

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

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

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

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

2.2 undo日志:数据回滚

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

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

2.3 更新缓冲池Buffer Pool的数据

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

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

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

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 深度理解mysql(二):InnnoDB存储引擎架构

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

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

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

深度理解mysql(二):InnnoDB存储引擎架构 因此当你提交事务成功后,此时做的数据的修改,一定已经刷新到磁盘中,这个时候如果mysql宕机,也会保证数据不丢失

2.6 binlog: 数据同步

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

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

binlog刷入磁盘的策略

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

深度理解mysql(二):InnnoDB存储引擎架构 这个参数有两个值

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

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

2.7 事务提交

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

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

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中的数据刷新到磁盘中,如图所示:

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

三.总结

通过本篇文章,就可以清晰地看到,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 ,要睡觉了困死了😴😴😴😴