全文建设在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版