阅读 263

InnoDB存储引擎更新数据流程

MySQL数据库在执行下面的语句是怎样的执行流程?

UPDATE t_user SET name = 'silly' where id =1; 复制代码

首先客户端通过网络传输将SQL语句发送给MySQL服务器,然后会经过SQL接口,解析器,优化器,执行器几个环节,解析SQL语句,生成执行计划,接着去由执行器负责这个计划的执行,调用InnoDB存储引擎的接口去执行 大致会走下图的这个流程

image.png

最终会通过存储引擎来查询数据,下面就探索下存储引擎InnoDB是如何完成一条语句的执行?

从下面一张官网的图片入手,来解析下InnoDB存储的结构设计

286774-cb2cb0651fed8fc5.png

上图中分为两部分:内存结构和磁盘结构

更新执行流程图

image.png

InnoDB重要内存结构:缓冲池(Buffer Pool)

缓冲池(Buffer Pool)会缓存很多的数据, 以便于以后在查询的时候,万一你要是内存缓冲池里有数据,就可以不用去查磁盘。 那么引擎在执行更新语句的时候,例如:id=1这一行数据,会将在缓冲池中查看数据是否存在,不存在的话,会将磁盘里的数据加载到缓存池中来,而且还会对这行记录加独占锁。

Undo日志文件:记录更新前的数据

在更新id=1这行数据库的时候,将name原来的值更新到现有值之前,会先将原来的值信息写入到undo日志文件中去,这样的话,如果在执行更新语句时,是在一个事务操作的话,在事务提交之前可以对数据进行回滚操作,那么就是通过Undo日志来讲值回滚到之前的值。

更新Buffer Pool中的缓存数据

当把更新的数据加入到缓存池中,并且加锁后,而且还把更新之前的旧值写入undo日志文件之后,就可以开始更新这行记录,更新的时候先是会更新缓存池中的数据,这个时候磁盘里的数据与缓存池的数据不一致,这个数据就是脏数据,因为是先更新缓存池中的数据,后面再将缓存池的数据刷盘到磁盘中,如果在这个时候宕机,那么缓存池的数据就会丢失,造成数据库事务提交后,服务重启前后数据不一致。

Redo Log Buffer:保证事务提交后不丢数据

在更新Buffer Pool内存的数据时,会先将内存所做的修改写入到一个Redo Log Buffer中去,这个也是内存的一个缓冲区,是用来存放redo日志的,所谓的redo日志,就是记录下来你对数据做了什么修改,比如对"id=1这行记录修改了name字段的值为xxx",这就是一个日志。在将修改数据写入 Redo Log Buffer中去的时候,这时候还没有提交事务,因为修改的数据写入Redo Log Buffer 也在内存中,此时MySQL奔溃,必然导致内存里Buffer Pool中修改的数据都丢失,同时写入Redo Log Buffer中的redo日志也会丢死。 因为还没有提交事务,就代表更新语句没有执行成功,这个时候MySQL宕机虽然内存里的数据都丢失了,但是磁盘上的数据还是原来的值,数据就没有任何的问题。 提交事务的时候,会根据一定的策略把Redo日志从Redo Log Buffer里刷入到磁盘文件里去,Redo事务日志刷盘策略通过innodb_flush_log_at_trx_commit来配置,有3中刷盘策略,默认策略为:1-实时写,实时刷

select @@innodb_flush_log_at_trx_commit; 复制代码

Redo事务日志刷盘策略

  • 0:延迟写。提交事务时不会将redo log写入os buffer,而是每隔1秒将redo log写入os buffer并调用fsync()刷入磁盘。系统崩溃会丢失一秒钟的数据。

  • 1:实时写,实时刷。每次提交事务都将redo log写入os buffer并调用fsync()刷入磁盘。这种方式系统奔溃不会丢失数据,因每次提交事务都写入磁盘,性能比较差

  • 2:实时写,延时刷。每次提交事务都将redo log写入os buffer,但并不会马上调用fsync()刷如磁盘,而是间隔1秒调fsync()刷盘。相对于每次提交都写盘和每隔1秒写盘,实施写os buffer延时刷盘是一个数据一致性与性能的之间的这种方案。

image.png

在这里又有疑问了,为什么不直接更新完成Buffer Pool的数据的时候要先将更新的数据写入Redo Log Buffer中,提交事务的时候将Redo Log Buffer的数据刷入磁盘,然后在异步将Buffer Pool, 同样Redo Log 写入磁盘也会费时间,何必多此一举?这个问题后面分析

BinLog日志:记录数据操作

Redo Log是一种偏向物理性质的重做日志,因为他里面记录的是类似这样的东西,"对哪个 数据页中的什么记录,做了个什么修改"。 而且redo log本身是属于InnoDB存储引擎特有的一个东西。 Binlog叫做归档日志,他里面记录的是偏向于逻辑性的日志,类似于"对users表中的id=1的一行数据做了更新操 作,更新以后的值是什么",Binlog不是InnoDB存储引擎特有的日志文件,是属于MySQL Server自己的日志文件。

提交事务的时候,同时会写入BinLog

在提交事务的时候,会把Redo Log日志刷入到磁盘文件中,其实在提交事务的时候同时还会把这次更新对应的BinLog日志写入到磁盘文件中。在这期间执行器跟InnoDB进行交互,会进行:加载数据到Buffer Pool中进行缓存、写入Undo日志、更新Buffer Pool里的数据、写入Redo Log Buffer、Redo Log 刷入磁盘、写BinLog等等

BinLog刷盘策略

select @@sync_binlog; 复制代码

通过sync_binlog 控制刷盘,取值范围0-N

  • 0:不强制要求刷盘,由系统自行判断什么时候将binlog写入磁盘;机器宕机,OS Cache里的BinLog日志会丢失

  • 1:每次提交事务就将binlog写入磁盘;提交事务后,BinLog日志不会丢失,但是性能稍微差点,系统默认值

  • n:每提交n个事务将binlog写入磁盘;

基于Binlog和Redo Log完成事务的提交

当把BinLog写入磁盘文件后,接着会完成最终事务提交,此时会把本次更新对应的BinLog文件名称和这次BinLog日志在文件里的位置,都写入到Redo Log日子文件里去,同时在Redo Log日志文件里写入一个Commit标记,才算最终完成事务的提交。

最后一步在redo日志中写入commit标记的意义是什么?

其实是用来保持Redo Log日志与BinLog日志一致性

  1. 当刚把Redo Logo刷入磁盘的时候,MySQL宕机了,这个时候因为没有最终的事务commit标记在Redo日志里,所以此次事务可以判定不成功,BinLog日志没有这次更新的日志,不会出现不一致的问题

  2. 当把BinLog日志写入磁盘后,此时MySQL宕机了,因为没有Redo Log中的最终commit标记,此时事务提交也是失败

必须是在Redo Log中写入最终的事务commit标记了,然后此时事务提交成功,而且redo log里有本次更新对应的日志,binlog里也有本次更新对应的日志 ,Redo Log和Binlog完全是一致的。

后台IO线程随机将内存更新后的脏数据刷回磁盘

MySQL有一个后台的IO线程,会在之后某个时间里,随机的把内存Buffer Pool中的修改后的脏数据给刷回到磁 盘上的数据文件里去,当IO线程把Buffer Pool里的修改后的脏数据刷回磁盘的之后,磁盘上的数据才会跟内存里一样,都是 name=xxx这个修改以后的值。 在IO线程把脏数据刷回磁盘之前,哪怕mysql宕机崩溃也没关系,因为重启之后,会根据Redo日志恢复之前提交事务做过的修改到内存里去,就是id=1的数据的name修改为了xxx,然后等适当时机,IO线程会把这个修改后的数据刷到磁盘上数据文件里去。


作者:silly8543
链接:https://juejin.cn/post/7025774541582368781


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐