Lever's Castle

关于日志的那些事

November 19, 2018

今天刚好看到了 MySQL 中日志系统的设计,谈谈自己的理解。

mysql 中有两个十分重要的日志模块 —— redo log 和 binlog。

redo log

顾名思义,是重做日志。redo log 是由 InnoDB 引擎实现的。

当有一条记录需要更新时,InnoDB 会把记录写到 redo log 里,并更新内存,更新就算完成了。之后 InnoDB 会在空闲的时候,把日志里的操作记录拿出来,更新到磁盘上。

这种技术就是 WAL - Write-Ahead Logging,即先写日志再写磁盘。

为什么要这样做呢?

首先如果没有 redo log ,更新操作是这样的:客户端发起更新请求 -> InnoDB 开始处理更新 -> 先从磁盘中找到对应的记录 -> 更新这条记录到磁盘中。整个过程 I/O 成本、查找成本都很高。

而有了 redo log,InnoDB 当下不需要到磁盘中查找对应的记录,只需要把这条更新行为记录到日志中,并更新内存中的数据即可。然后等空闲的时候,再从 redo log 中拿出那些待处理的日志开始处理,这中间甚至可能会有一些重复的操作,也可以顺便进行合并,效率提高了不是一点点啊。

redo log 的一些细节

InnoDB 的 redo log 是固定大小的,也就是说一旦写满了,InnoDB 就需要提前开始清算工作,把一部分日志对应的操作落实到磁盘上,以便释放这部分空间让后面的日志能够写进来。

有了 redo log,InnoDB 就能够应对异常重启问题,之前提交的记录都不会丢失,这个能力称为 crashed-safe。

binlog

redo log 属于 InnoDB 引擎层的日志,是物理日志,而 binlog 属于 mysql server 层面的日志,是逻辑日志。

binlog 记录的是原始操作,并且可以不断追加写入,不需要覆盖以前的日志

一个数据的更新操作可以描述如下:

  1. 先找到对应要被更新的记录,优先在内存中找,找不到的话再从磁盘读入到内存中返回
  2. 更新数据结果到内存中,同时将这个操作记录到 redo log 中,此时 redo log 处于 prepare 状态,然后告知执行器执行完成,随时可以提交事务。
  3. 执行器生成这个对应的 binlog ,并写入到磁盘中
  4. 执行器调用引擎的事务提交接口,引擎把刚刚的 redo log 记录更新为 commit 状态。

这里使用到了两阶段提交,来保证事务的一致性。 也就是说,redo log 所记录的行为必须与 binlog 所记录的行为一致才行,两个日志不一致,最终一定会导致数据不一致。

启发

  1. 不要不重视日志,在很多系统中,日志也极为重要
  2. 在程序设计中,我们也有很多地方可以借鉴 redo log 和 binlog 的设计,将一些耗时耗性能的事情放到空闲时候做。感觉有点类似于生产者 - 消费者模式,比如我们分析用户行为,可以把用户行为先写入到日志中,之后再由别的程序消费日志中的数据,分析用户行为。
  3. 日志可以用来重放某一时间段内发生的事情,保留日志我们能够复原现场。

    最后

    关于 mysql 日志的知识点是从专栏 —— MySQL实战45讲 中看到的,作者原文讲的很棒,推荐你也去订阅看看。

图片


Lever

痕迹
没有过去,就没法认定现在的自己