MYSQL实战优化——InnoDB存储引擎介绍

InnoDB存储引擎介绍

InnoDB的重要内存结构:缓冲池
首先假设我们有一条SQL语句是这样的:

update users set name = 'xxx' where id = 10

InnoDB存储引擎中有一个非常重要的放在内存里的组件,就是缓冲池(Buffer pool),这里面会缓存很多的数据,以便于以后在查询的时候,万一你要是内存缓冲池里有数据,就可以不用去查磁盘了。比如,当我们的InnoDB存储引擎要执行更新语句的时候,比如对“id=10”这一行数据,它其实会先看看“id=10”这一行数据是否在缓冲池里,如果不在的话,那么会直接从磁盘里加载到缓冲池来,而且还会对这行记录加独占锁。

undo日志文件:如何让你更新的数据可以回滚?
假设“id=10”这行数据的name原来是“zhangsan”,现在我们要更新为“xxx”,那么此时我们得先把要更新的原来的值“zhangsan”和“id=10”这些信息,写入到undo日志文件中去。我们大家都知道,如果执行一个更新语句,要是它在一个事务里的话,那么事务提交之前我们都是可以对数据进行回滚的,也就是把更新为“xxx”的值回滚到之前的“zhangsan”去。所以为了考虑到未来可能要回滚数据的需要,这里会把更新前的值写入undo日志文件,我们看下图:

undo_1.jpg

更新buffer pool中的缓存数据
当我们把要更新的那行记录从磁盘文件加载到缓冲池,同时对它加锁,而且还把更新前的旧值写入undo日志文件之后,我们就可以正式开始更新这行记录了,更新的时候先是会更新缓冲池中的记录,此时这个数据就是脏数据。这里所谓的更新内存缓冲池里的数据,意思就是把内存里的“id=10”这行数据的name字段修改为“xxx”,那为什么说此时这行数据就是脏数据了呢?因为这个时候磁盘上“id=10”这行数据的name字段还是“zhangsan”,但是内存里这行数据已经被修改了,所以就会叫它是脏数据。

Redo Log Buffer:万一系统宕机,如何避免数据丢失?
我们现在思考这样一个问题,按照上面的描述,现在已经把内存里的数据进行了修改,但是磁盘上的数据还没修改,那么此时万一MySQL所在的机器宕机了,必然会导致内存里修改过的数据丢失,这怎么办呢?这个时候,就必须要把对内存所做的修改写入到一个Redo Log Buffer里去,这也是内存里的一个缓冲区,是用来存放redo日志的。所谓的redo日志,就是记录下来你对数据做了什么修改,比如对“id=10”这行记录修改了name字段的值为“xxx”,这就是一个日志。我先看下图的示意:

redo_1.jpg

这个redo日志其实是用来在MySQL突然宕机的时候,用来恢复更新过的数据的,但是我们现在还没法直接讲解redo是如何做的,毕竟现在redo日志还仅仅停留在内存缓冲里。

如果还没提交事务,MySQL宕机了怎么办?
我都知道在数据库中,哪怕执行一条SQL语句,其实也可以是一个独立的事务,只有当你提交事务之后,SQL语句才算执行结束。到目前为止,其实还没有提交事务,那么此时如果MySQL服务器宕机,必然导致内存里Buffer Pool中的修改过的数据都丢失,同时写入Redo Log Buffer中的redo日志也会丢失。那么此时数据丢失要紧吗?其实是不要紧的,因为你一条更新语句,没提交事务,就代表它没执行成功,此时MySQL宕机虽然导致内存里的数据都丢失了,但是你会发现,磁盘上的数据依然还停留在原样子。所以此时你的这个事务就是执行失败了,你会收到一个数据库的异常。然后MySQL重启之后,你会发现你的数据并没有任何变化。

提交事务的时候将redo日志写入磁盘中
现在我们要提交一个事务了,此时就会根据一定的策略把redo日志从redo log buffer里刷入到磁盘文件里。此时这个策略是通过innodb_flush_log_at_trx_commit来配置的,它有几个选项:
1、innodb_flush_log_at_trx_commit=0,提交事务的时候不会把redo log buffer里的数据刷入磁盘文件,如果MySQL宕机,此时内存里的数据全部丢失。相当于事务提交成功了,但是由于MySQL突然宕机,导致内存中的数据和redo日志都丢失。
2、innodb_flush_log_at_trx_commit=1,提交事务的时候就必须把redo log buffer从内存刷入到磁盘文件里去,只要事务提交成功,那么redo log就必然在磁盘文件里了。这个时候MySQL系统突然宕机,此时数据会丢失吗?显然不会,因为虽然内存里的修改成name=xxx的数据丢失了,但是redo日志里已经记录了对某某数据做了name=xxx的修改,所以重启MySQL之后,它可以根据redo日志去恢复之前做过的修改。如下图:

redo2.jpg

3、innodb_flush_log_at_trx_commit=2,提交事务的时候把redo日志写入磁盘文件对应的os cache缓存里去,而不是直接进入磁盘文件,可能1秒后才会把os cache里的数据写入到磁盘文件里去。这种模式下,提交事务之后,redo log可能仅仅停留在os cache内存缓存里,没实际进入磁盘文件,万一此时你要是机器宕机了,那么os cache里的redo log就会丢失,同样会让你感觉提交事务了,结果数据丢了。

那么三种策略怎么选择呢?我的建议是设置为1,也就是说提交事务的时候,redo日志必须是刷入磁盘文件的。这样可以严格保证提交事务之后,数据是绝对不会丢失的,因为有redo日志在磁盘文件里可以恢复你做的所有修改。当然,如果对数据完整性要求不高的话可以选择0或者2。

MySQL binlog到底是什么东西?
我们之前说的redo log是一种偏向物理性质的重做日志,因为它里面记录的是类似这样的东西,“对哪个数据页中的什么记录,做了一个什么修改”。而binlog叫做归档日志,它里面记录的是偏向于逻辑性的日志,类似于“对users表中的id=10的一行数据做了更新操作,更新以后的值是什么”。binlog不是InnoDB存储引擎特有的日志文件,是属于MySQL server自己的日志文件。

\color{green}{提交事务的时候,同时会写入binlog}
之前我们讲到,在提交事务的时候会把redo log日志写入磁盘文件里,其实在提交事务的时候我们还会把这次更新对应的binlog日志写入到磁盘文件中去。如下图所示:

binlog2.jpg

大家可以在这个图里看到一些变动,就是我们把之前跟InnoDB存储引擎进行交互的组件加入了进来,这个组件就是之前提到的执行器,它负责跟InnoDB进行交互,包括从磁盘文件里加载数据到buffer pool中进行缓存,包括写入undo日志,包括更新buffer pool里的数据,以及写入redo log buffer,redo log刷入磁盘,写binlog等待。实际上,执行器是非常核心的一个组件,负责跟存储引擎配合完成一个SQL语句在磁盘与内存层面的全部数据更新操作。而且我们在上图可以看到,我把一次更新语句的执行,拆分为了两个阶段,上图中的1、2、3、4几个步骤,其实本质是执行这个更新语句的时候做的事。然后上图中的5和6两个步骤,是从你提交事务开始的,属于提交事务的阶段了。

\color{green}{binlog日志的刷盘策略分析}
对于binlog日志,其实也有不同的刷盘策略,有一个sync_binlog参数可以控制binlog的刷盘策略,它的默认值是0,此时你把binlog写入磁盘的时候其实不是直接进入磁盘文件,而是进入os cache内存缓存。所以跟之前分析的一样,如果此时机器宕机,那么你在os cache里的binlog日志是会丢失的,我们看下图:

binlog3.jpg

如果要是把sync_binlog参数设置为1的话,那么此时会强制在提交事务的时候,把binlog直接写入到磁盘文件里,那么这样提交事务之后,哪怕机器宕机,磁盘上的binlog也是不会丢失的。

\color{green}{基于binlog和redo log完成事务的提交}
当我们把binlog写入磁盘文件之后,接着就会完成最终的事务提交,此时会把本次更新对应的binlog文件名称和binlog日志在文件里的位置都写入到redo log文件里去,同时在redo log文件里写入一个commit标记。完成这个事情之后,才算最终完成了事务的提交。我们看下面的示意图:

binlog4.jpg

\color{green}{最后一步在redo日志中写入commit标记的意义是什么?}
其实是用来保持redo log日志与binlog日志一致的。我们来举个例子,假设我们在提交事务的时候,一共有上图中的5、6、7三个步骤,必须是三个步骤都执行完毕才算是提交了事务。那么在我们刚完成步骤5的时候,也就是redo log刚刷入磁盘文件的时候MySQL宕机了,此时怎么办?这个因为没有最终的事务commit标记在redo日志里,所以此次事务可以判定为不成功,不会说redo日志文件里有这次更新的日志,但是binlog日志文件里没有这次更新的日志,不会出现数据不一致的问题。

如果完成步骤6的时候此时MySQL宕机了,怎么办?同理,因为没有redo log中的最终commit标记,因此此时事务提交也是失败的。

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

\color{green}{后台IO线程随机将内存更新后的脏数据刷入磁盘}
现在假设已经提交事务了,此时一次更新“update users set name = 'xxx' where id=10”,它已经把内存里的buffer pool中的缓存数据更新了,同时磁盘里也存入了redo日志和binlog日志,其中记录了我们指定的把“id=10”这行数据修改了“name='xxx'”。但是这个时候磁盘上的数据文件里的“id=10”这行数据的name字段还是等于“zhangsan”这个旧值。所以MySQL有一个\color{blue}{后台的IO线程},会在之后某个时间里,随机的把内存buffer pool中的修改后的脏数据给刷回到磁盘上的数据文件里去,我们看下图:

commit1.jpg

当IO线程把buffer pool里的修改后的脏数据刷回磁盘之后,磁盘上的数据才会跟内存里一样。在IO线程把脏数据刷回磁盘之前,哪怕MySQL宕机也没有关系,因为重启之后,会根据redo日志恢复之前提交事务做过的修改到内存里去,然后等适当时机,IO线程自然还是会把这个修改后的数据刷到磁盘上的数据文件里去。

\color{green}{基于更新数据的流程,总结一下InnoDB存储引擎的架构原理}
大家通过一次更新数据的流程,就可以清晰地看到InnoDB存储引擎主要就是包含了一些buffer pool、redo log buffer等内存里的缓存数据,同时还包含了一些undo日志文件,redo日志文件等东西,同时MySQL server自己还有binlog日志文件。在你执行更新的时候,每条SQL语句都会对应修改buffer pool里的缓存数据、写undo日志、写redo log buffer几个步骤。但是当你提交事务的时候,一定会把redo log刷入磁盘,binlog刷入磁盘,完成redo log中的事务commit标记。最后,后台的IO线程会随机的把buffer pool里的脏数据输入磁盘里去。

\color{green}{思考题:执行更新操作的时候,为什么不能执行修改磁盘上的数据呢}
1、为什么MySQL在更新数据的时候要大费周章的搞这么多事情,包括buffer pool、redo log、undo log、binlog、事务提交、脏数据。引入了一大堆的概念,有这么多复杂的流程和步骤。
2、为什么它反而最关键的修改磁盘里的数据,要通过IO线程不定时的去执行?
3、为什么它不干脆直接就每次执行SQL语句,直接就更新磁盘里的数据得了?

\color{green}{本文结束}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352