1. 架构图

MySQL存储引擎有很多包括MyISAM、InnoDB、MEMORY。本文主要介绍InnoDB存储引擎,下面是InnoDB 存储引擎的架构图。
MySQL学习(二)-- InnoDB存储引擎 - 图1

2. 缓冲池

缓冲池(Buffer Pool) 是InnoDB中一个重要的内存结构。比如我们事实上在我们新”id=10”这一行数据,它会先看”id=10”这一行数据看看是否在缓冲池中,如果不在的话,那么会直接从磁盘里加载到缓冲池里来,而且接着还会对这行记录加独占锁。

3.uodo日志

在我们将 “id=10” 这行的name 更新为”xxx” 的时候,其实我们会将原来的值,写入到undo日志中去。为什么,因为InnoDB是支持事务的,而事务的回滚就需要将值恢复到之前的状态,这时候就需要undo日志参与。

4. 更新buffer pool 中的缓存数据

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

5. Redo Log Buffer

因为脏数据的存在,如果此时MySQL宕机了,那么我们修改的操作白做了。怎么解决呢,其实在对内存数据做修改的时候,会把修改的记录写入到Redo Log Buufer里去。

这个redo日志就是用来在MySQL突然宕机的时候,用来恢复你更新过的数据的,但是我们很快又会发现一个问题,就是Redo Log日志不是也在内存中么,宕机同样会丢失呀。
其实在提交的事务的时候,redo日志会根据一定的策略把redo 日志从redo log buffer 里刷入到磁盘文件里去。
此时这个策略是通过innodb_flush_log_at_trx_commit 来配置的。
它有以下几种配置:

  • 值为0:在提交事务的时候,不会将redo log 日志刷入到磁盘
  • 值为1:在提交事务的时候,必须把redo log刷入到磁盘文件中去
  • 值为2:在提交事务的时候,先将redo log日志刷入到os cache缓存中去,1秒后在刷入到磁盘。
    那么在我们实际使用中,使用哪种方式呢,我们一般使用的是第二种,毕竟第一种和第三种都不保险,如果宕机都会导致更新数据丢失的问题。

6. MySQL binlog

小乌龟已经做数据同步工具的时候,一直听说binlog 日志的大名,但是没有详细了解过是个啥东西。其实它和redo log 有点像。

redo log是一种偏向物理性质的重做日志,因为他里面记录的是 对哪个数据页中的什么记录做了什么修改。redo log 本身属于InnoDB 存储引擎的一个东西。

而binlog 叫做归档日志,它里面记录的是偏向于逻辑性的日志,类似于”对users 表的id=10 的一行做了更新,以及更新后的值是什么”
binlog 不是InnoDB 存储特有的日志文件,是属于mysql server自己的日志。

在上一节我们说过提交的事务的时候,会把redo 日志刷入到磁盘,其实这个时候,也会在磁盘文件中写入binlog日志。

那么对于binlog来说,是每次条log日志都同步刷入到磁盘中么。其实不是的,它和redo log 一样,也有不同的刷盘策略。有一个sync_binlog 参数配置,可以选择不同的策略。

  • 值为0: 将日志先写入到os_cache,同样不能解决宕机导致丢失的问题。
  • 值为1: 强制在提交事务的时候,把binlog 写入到磁盘文件中去

7. binlog 和 redo log 协作

那么binlog 和 redo log 会不会存在不一致的风险呢,其实不会的。我们先看看事务最终提交的流程。

当我们把binlog 日志写入到磁盘文件之后,接着就会完成最终的事务提交,此时会把本次更新对应的binlog文件名称和这次更新的binlog 日志在文件里的位置,都写入到redo log日志文件里,同时在redo log日志文件里写入一个commit 标记。

就是有了最后一步写入commit标记保证了两个日志的一致性,为何这么说。因为只有redo log 中出现这个标志,才能说明事务是成功的,此时两个日志的数据都是完整的。

8. 后台线程将脏数据刷回磁盘

上文我们有提到buffer pool 中的数据可能与磁盘文件中的数据不一致,那么合适将buffer pool 中的数据刷入到磁盘中去呢。其实MySQL 会有一个后台的IO线程,定时将脏数据刷入到磁盘中去。

9. 参考