乐趣区

关于mysql:3000帧动画图解MySQL为什么需要binlogredo-log和undo-log

全文建设在 MySQL 的存储引擎为 InnoDB 的根底上

先看一条 SQL 如何入库的:

这是一条很简略的更新 SQL,从 MySQL 服务端接管到 SQL 到落盘,先后通过了 MySQL Server 层和 InnoDB 存储引擎。

  1. Server 层就像一个产品经理,剖析客户的需要,并给出实现需求的计划。
  2. InnoDB 就像一个基层程序员,实现产品经理给出的具体计划。

在 MySQL”剖析需要,实现计划“的过程中,还夹杂着内存操作和磁盘操作,以及记录各种日志。

他们到底有什么用途?他们之间到底怎么配合的?MySQL 又为什么要分层呢?InnoDB 外面的那一块 Buffer Pool 又是什么?

咱们缓缓剖析。

分层构造

MySQL 为什么要分为 Server 层和存储引擎两层呢?

这个问题官网也没有给出明确的答案,然而也不难猜,简略来说就是为了“解耦”。

Server 层和存储引擎各司其职,分工明确,用户能够依据不同的需要去应用适合的存储引擎,多好的设计,对不对?

起初的倒退也验证了“分层设计”的优越性:MySQL 最后搭载的存储引擎是自研的只反对简略查问的 MyISAM 的前身 ISAM,起初与 Sleepycat 单干研发了 Berkeley DB 引擎,反对了事务。江山代有才人出,技术后浪推前浪,MySQL 在继续的降级着本人的存储引擎的过程中,遇到了横空出世的 InnoDB,InnoDB 的功能强大让 MySQL 倍感压力。

本人的存储引擎打不过 InnoDB 怎么办?

打不过就退出!

MySQL 抉择了和 InnoDB 单干。正是因为 MySQL 存储引擎的插件化设计,两个公司单干的十分顺利,MySQL 也在单干后不久就公布了正式反对 nnoDB 的 4.0 版本以及经典的 4.1 版本。

MySQL 合并天下模式也成为 MySQL 走向凋敝的一个重要因素。这能让 MySQL 短暂地放弃着极强竞争力。时至今日,MySQL 仍然占据着极高数据库市场份额,仅次于王牌数据库 Oracle。

Buffer Pool

在 InnoDB 里,有一块十分重要的构造——Buffer Pool。

Buffer Pool 是个什么货色呢?

Buffer Pool 就是一块用于缓存 MySQL 磁盘数据的内存空间。

为什么要缓存 MySQL 磁盘数据呢?

咱们通过一个例子阐明,咱们先假如没有 Buffer Pool,user 表外面只有一条记录,记录的 age = 1,假如须要执行三条 SQL:

  1. 事务 A:update user set age = 2
  2. 事务 B:update user set age = 3
  3. 事务 C:update user set age = 4

如果没有 Buffer Pool,那执行就是这样的:

从图上能够看出,每次更新都须要从磁盘拿数据(1 次 IO),批改完了须要刷到磁盘(1 次 IO),也就是每次更新都须要 2 次磁盘 IO。三次更新须要 6 次磁盘 IO。

而有了 Buffer Pool,执行就成了这样:

从图上能够看出,只须要在第一次执行的时候将数据从磁盘拿到 Buffer Pool(1 次 IO),第三次执行完将数据刷回磁盘(1 次 IO),整个过程只须要 2 次磁盘 IO,比没有 Buffer Pool 节俭了 4 次磁盘 IO 的工夫。

当然,Buffer Pool 真正的运行流程没有这么简略,具体实现细节和优化技巧还有很多,因为篇幅无限,本文不做详细描述。

我想表白的是:Buffer Pool 就是将磁盘 IO 转换成了内存操作,节俭了工夫,进步了效率。

Buffer Pool 是进步了效率没错,然而呈现了一个问题,Buffer Pool 是基于内存的,而只有一断电,内存外面的数据就会全副失落。

如果断电的时候 Buffer Pool 的数据还没来得及刷到磁盘,那么这些数据就失落了吗?

还是下面的那个例子,如果三个事务执行结束,在 age = 4 还没有刷到磁盘的时候,忽然断电,数据就全副丢掉了:

试想一下,如果这些失落的数据是外围的用户交易数据,那用户能承受吗?

答案是否定的。

那 InnoDB 是如何做到数据不会失落的呢?

明天的第一个日志——redo log 退场了。

复原 – redo log

顾名思义,redo 是重做的意思,redo log 就是重做日志的意思。

redo log 是如何保证数据不会失落的呢?

就是在批改之后,先将批改后的值记录到磁盘上的 redo log 中,就算忽然断电了,Buffer Pool 中的数据全副失落了,复电的时候也能够依据 redo log 复原 Buffer Pool,这样既利用到了 Buffer Pool 的内存高效性,也保障了数据不会失落。

咱们通过一个例子阐明,咱们先假如没有 Buffer Pool,user 表外面只有一条记录,记录的 age = 1,假如须要执行一条 SQL:

  1. 事务 A:update user set age = 2

执行过程如下:

如上图,有了 redo log 之后,将 age 批改成 2 之后,马上将 age = 2 写到 redo log 外面,如果这个时候忽然断电内存数据失落,在复电的时候,能够将 redo log 外面的数据读出来复原数据,用这样的形式保障了数据不会失落。

你可能会问,redo log 文件也在磁盘上,数据文件也在磁盘上,都是磁盘操作,何必多此一举?为什么不间接将批改的数据写到数据文件外面去呢?

傻瓜,因为 redo log 是磁盘程序写,数据刷盘是磁盘随机写,磁盘的程序写比随机写高效的多啊。

这种先预写日志前面再将数据刷盘的机制,有一个高大上的专业名词——WAL(Write-ahead logging),翻译成中文就是预写式日志。

尽管磁盘程序写曾经很高效了,然而和内存操作还是有肯定的差距。

那么,有没有方法进一步优化一下呢?

答案是能够。那就是给 redo log 也加一个内存 buffer,也就是 redo log buffer,用这种套娃式的办法进一步提高效率。

redo log buffer 具体是怎么配合刷盘呢?

在这个问题之前之前,咱们先来捋一下 MySQL 服务端和操作系统的关系:

MySQL 服务端是一个过程,它运行于操作系统之上。也就是说,操作系统挂了 MySQL 肯定挂了,然而 MySQL 挂了操作系统不肯定挂。

所以 MySQL 挂了有两种状况:

  1. MySQL 挂了,操作系统也挂了,也就是常说的服务器宕机了。这种状况 Buffer Pool 外面的数据会全副失落,操作系统的 os cache 外面的数据也会失落。
  2. MySQL 挂了,操作系统没有挂。这种状况 Buffer Pool 外面的数据会全副失落,操作系统的 os cache 外面的数据不会失落。

OK,理解了 MySQL 服务端和操作系统的关系之后,再来看 redo log 的落盘机制。redo log 的刷盘机制由参数 innodb_flush_log_at_trx_commit 管制,这个参数有 3 个值能够设置:

  1. innodb_flush_log_at_trx_commit = 1:实时写,实时刷
  2. innodb_flush_log_at_trx_commit = 0:提早写,提早刷
  3. innodb_flush_log_at_trx_commit = 2:实时写,提早刷

写能够了解成写到操作系统的缓存(os cache),刷能够了解成把操作系统外面的缓存刷到磁盘。

这三种策略的区别,咱们离开探讨:

innodb_flush_log_at_trx_commit = 1:实时写,实时刷

这种策略会在每次事务提交之前,每次都会将数据从 redo log 刷到磁盘中去,实践上只有磁盘不出问题,数据就不会失落。

总结来说,这种策略效率最低,然而丢数据危险也最低。

innodb_flush_log_at_trx_commit = 0:提早写,提早刷

这种策略在事务提交时,只会把数据写到 redo log buffer 中,而后让后盾线程定时去将 redo log buffer 外面的数据刷到磁盘。

这种策略是最高效的,然而咱们都晓得,定时工作是有间隙的,然而如果事务提交后,后盾线程没来得及将 redo log 刷到磁盘,这个时候不论是 MySQL 过程挂了还是操作系统挂了,这一部分数据都会失落。

总结来说这种策略效率最高,丢数据的危险也最高。

innodb_flush_log_at_trx_commit = 2:实时写,提早刷

这种策略在事务提交之前会把 redo log 写到 os cache 中,但并不会实时地将 redo log 刷到磁盘,而是会每秒执行一次刷新磁盘操作。

这种状况下如果 MySQL 过程挂了,操作系统没挂的话,操作系统还是会将 os cache 刷到磁盘,数据不会失落,如下图:

但如果 MySQL 所在的服务器挂掉了,也就是操作系统都挂了,那么 os cache 也会被清空,数据还是会失落。如下图:

所以,这种 redo log 刷盘策略是下面两种策略的折中策略,效率比拟高,失落数据的危险比拟低,绝大多状况下都举荐这种策略。

总结一下,redo log 的作用是用于复原数据,写 redo log 的过程是磁盘程序写,有三种刷盘策略,有 innodb_flush_log_at_trx_commit 参数管制,举荐设置成 2。

回滚 – undo log

咱们都晓得,InnoDB 是反对事务的,而事务是能够回滚的。

如果一个事务将 age= 1 批改成了 age=2,在事务还没有提交的时候,后盾线程曾经将 age= 2 刷入了磁盘。这个时候,不论是内存还是磁盘上,age 都变成了 2,如果事务要回滚,找不到批改之前的 age=1,无奈回滚了。

那怎么办呢?

很简略,把批改之前的 age= 1 存起来,回滚的时候依据存起来的 age= 1 回滚就行了。

MySQL 的确是这么干的!这个记录批改之前的数据的过程,叫做记录 undo log。undo 翻译成中文是撤销、回滚的意思,undo log 的次要作用也就是回滚数据。

如何回滚呢?看上面这个图:

MySQL 在将 age = 1 批改成 age = 2 之前,先将 age = 1 存到 undo log 外面去,这样须要回滚的时候,能够将 undo log 外面的 age = 1 读出来回滚。

须要留神的是,undo log 默认存在全局表空间外面,你能够简略的了解成 undo log 也是记录在一个 MySQL 的表外面,插入一条 undo log 和插入一条一般数据是相似。也就是说,写 undo log 的过程中同样也是要写入 redo log 的。

归档 – binlog

undo log 记录的是批改之前的数据,提供回滚的能力。

redo log 记录的是批改之后的数据,提供了解体复原的能力。

那 binlog 是干什么的呢?

binlog 记录的是批改之后的数据,用于归档。

和 redo log 日志相似,binlog 也有着本人的刷盘策略,通过 sync_binlog 参数管制:

  • sync_binlog = 0:每次提交事务前将 binlog 写入 os cache,由操作系统管制什么时候刷到磁盘
  • sync_binlog =1:采纳同步写磁盘的形式来写 binlog,不应用 os cache 来写 binlog
  • sync_binlog = N:当每进行 n 次事务提交之后,调用一次 fsync 将 os cache 中的 binlog 强制刷到磁盘

那么问题来了,binlog 和 redo log 都是记录的批改之后的值,这两者有什么区别呢?有 redo log 为什么还须要 binlog 呢?

首先看两者的一些区别:

  • binlog 是逻辑日志,记录的是对哪一个表的哪一行做了什么批改;redo log 是物理日志,记录的是对哪个数据页中的哪个记录做了什么批改,如果你还不理解数据页,你能够了解成对磁盘上的哪个数据做了批改。
  • binlog 是追加写;redo log 是循环写,日志文件有固定大小,会笼罩之前的数据。
  • binlog 是 Server 层的日志;redo log 是 InnoDB 的日志。如果不应用 InnoDB 引擎,是没有 redo log 的。

但说实话,我感觉这些区别并不是 redo log 不能取代 binlog 的起因,MySQL 官网齐全能够调整 redo log 让他合并 binlog 的能力,但他没有这么做,为什么呢?

我认为不必 redo log 取代 binlog 最大的起因是“没必要”。

为什么这么说呢?

第一点,binlog 的生态曾经建设起来。MySQL 高可用次要就是依赖 binlog 复制,还有很多公司的数据分析系统和数据处理系统,也都是依赖的 binlog。取代 binlog 去扭转一个生态费劲了不讨好。

第二点,binlog 并不是 MySQL 的瓶颈,花工夫在没有瓶颈的中央没必要。

总结

总结一下:

  1. Buffer Pool 是 MySQL 过程治理的一块内存空间,有缩小磁盘 IO 次数的作用。
  2. redo log 是 InnoDB 存储引擎的一种日志,次要作用是解体复原,有三种刷盘策略,有 innodb_flush_log_at_trx_commit 参数管制,举荐设置成 2。
  3. undo log 是 InnoDB 存储引擎的一种日志,次要作用是回滚。
  4. binlog 是 MySQL Server 层的一种日志,次要作用是归档。
  5. MySQL 挂了有两种状况:操作系统挂了 MySQL 过程跟着挂了;操作系统没挂,然而 MySQL 过程挂了。

最初,再用一张图总结一下全文的知识点:

写在最初

这篇文章写在一年之前,原本感觉是一篇水文没想要发,最近无聊批改了一下发了进去,心愿可能用动图的模式帮忙到 MySQL 根底不太好的敌人,大神疏忽就好。

须要强调的一点是,因为作者程度无限,本文只是通俗的从无到有地论述了 MySQL 几种日志的大抵作用,过程中省略了很多细节,比方 Buffer Pool 的实现细节,比方 undo log 和 MVCC 的关系,比方 binlog buffer、change buffer 的存在,比方 redo log 的两阶段提交。

如果您有任何问题,咱们能够探讨,如果您在文中发现错误,还望您指出,万分感激!

好了,明天的文章就到这里了。

感激你的浏览!我是 CoderW,咱们下期再见。

最初,欢送关注我的公众号“CoderW”一起探讨提高~~~~

参考资料

  • 《MySQL 实战 45 讲》
  • 《从根儿上了解 MySQL》
  • 《MySQL 技术底细—InnoDB 存储引擎》第 2 版
退出移动版