39讲答疑课堂:MySQL中InnoDB的知识点串讲 - 图139讲答疑课堂:MySQL中InnoDB的知识点串讲

你好,我是刘超。

39讲答疑课堂:MySQL中InnoDB的知识点串讲 - 图2模块六有关数据库调优的内容到本周也正式结束了,今天我们⼀起串下MySQL中InnoDB的知识点。InnoDB存储引擎作为我们最常⽤到的存储引擎之⼀,充分熟悉它的的实现和运⾏原理,有助于我们更好地创建和维护数据库表。

InnoDB体系架构

InnoDB主要包括了内存池、后台线程以及存储⽂件。内存池⼜是由多个内存块组成的,主要包括缓存磁盘数据、redo log缓冲等;后台线程则包括了Master Thread、IO Thread以及Purge Thread等;由InnoDB存储引擎实现的表的存储结构⽂件⼀般包括表结构⽂件(.frm)、共享表空间⽂件(ibdata1)、独占表空间⽂件(ibd)以及⽇志⽂件(redo⽂件等)等。

39讲答疑课堂:MySQL中InnoDB的知识点串讲 - 图3

内存池

我们知道,如果客户端从数据库中读取数据是直接从磁盘读取的话,⽆疑会带来⼀定的性能瓶颈,缓冲池的作⽤就是提⾼整个数据库的读写性能。

客户端读取数据时,如果数据存在于缓冲池中,客户端就会直接读取缓冲池中的数据,否则再去磁盘中读取;对于数据库中的修改数据,⾸先是修改在缓冲池中的数据,然后再通过Master Thread线程刷新到磁盘上。

理论上来说,缓冲池的内存越⼤越好。我们在第38讲中详细讲过了缓冲池的⼤⼩配置⽅式以及调优。

缓冲池中不仅缓存索引⻚和数据⻚,还包括了undo⻚,插⼊缓存、⾃适应哈希索引以及InnoDB的锁信息等等。

InnoDB允许多个缓冲池实例,从⽽减少数据库内部资源的竞争,增强数据库的并发处理能⼒,第38讲还讲到了缓冲池实例的配置以及调优。

InnoDB存储引擎会先将重做⽇志信息放⼊到缓冲区中,然后再刷新到重做⽇志⽂件中。

后台线程

Master Thread 主要负责将缓冲池中的数据异步刷新到磁盘中,除此之外还包括插⼊缓存、undo⻚的回收等,IO Thread是负责读写IO的线程,⽽Purge Thread主要⽤于回收事务已经提交了的undo log,Pager Cleaner Thread是新引⼊的⼀个⽤于协助Master Thread刷新脏⻚到磁盘的线程,它可以减轻Master Thread的⼯作压⼒,减少阻塞。

存储⽂件

在MySQL中建⽴⼀张表都会⽣成⼀个.frm⽂件,该⽂件是⽤来保存每个表的元数据信息的,主要包含表结构定义。

在InnoDB中,存储数据都是按表空间进⾏存放的,默认为共享表空间,存储的⽂件即为共享表空间⽂件(ibdata1)。若设置了参数innodb_file_per_table为1,则会将存储的数据、索引等信息单独存储在⼀个独占表空间,因此也会产⽣⼀个独占表空 间⽂件(ibd)。如果你对共享表空间和独占表空间的理解还不够透彻,接下来我会详解。

⽽⽇志⽂件则主要是重做⽇志⽂件,主要记录事务产⽣的重做⽇志,保证事务的⼀致性。

InnoDB逻辑存储结构

InnoDB逻辑存储结构分为表空间(Tablespace)、段(Segment)、区(Extent)、⻚Page)以及⾏(row)。
39讲答疑课堂:MySQL中InnoDB的知识点串讲 - 图4

表空间(Tablespace)

InnoDB提供了两种表空间存储数据的⽅式,⼀种是共享表空间,⼀种是独占表空间。 InnoDB 默认会将其所有的表数据存储在⼀个共享表空间中,即ibdata1。

我们可以通过设置innodb_file_per_table参数为1(1代表独占⽅式)开启独占表空间模式。开启之后,每个表都有⾃⼰独⽴的表空间物理⽂件,所有的数据以及索引都会存储在该⽂件中,这样⽅便备份以及恢复数据。

段(Segment)

表空间是由各个段组成的,段⼀般分为数据段、索引段和回滚段等。我们知道,InnoDB默认是基于B +树实现的数据存储。

这⾥的索引段则是指的B +树的⾮叶⼦节点,⽽数据段则是B +树的叶⼦节点。⽽回滚段则指的是回滚数据,之前我们在讲事务隔离的时候就介绍到了MVCC利⽤了回滚段实现了多版本查询数据。

区(Extent) / ⻚(Page)

区是表空间的单元结构,每个区的⼤⼩为1MB。⽽⻚是组成区的最⼩单元,⻚也是InnoDB存储引擎磁盘管理的最⼩单元,每个⻚的⼤⼩默认为16KB。为了保证⻚的连续性,InnoDB存储引擎每次从磁盘申请4-5个区。

⾏(Row)

InnoDB存储引擎是⾯向列的(row-oriented),也就是说数据是按⾏进⾏存放的,每个⻚存放的⾏记录也是有硬性定义的,最

多允许存放16KB/2-200⾏,即7992⾏记录。

InnoDB事务之redo log⼯作原理

InnoDB是⼀个事务性的存储引擎,⽽InnoDB的事务实现是基于事务⽇志redo log和undo log实现的。redo log是重做⽇志,提供再写⼊操作,实现事务的持久性;undo log是回滚⽇志,提供回滚操作,保证事务的⼀致性。

redo log⼜包括了内存中的⽇志缓冲(redo log buffer)以及保存在磁盘的重做⽇志⽂件(redo log file),前者存储在内存中,容易丢失,后者持久化在磁盘中,不会丢失。

InnoDB的更新操作采⽤的是Write Ahead Log策略,即先写⽇志,再写⼊磁盘。当⼀条记录更新时,InnoDB会先把记录写⼊到redo log buffer中,并更新内存数据。我们可以通过参数innodb_flush_log_at_trx_commit⾃定义commit时,如何将redo log
buffer中的⽇志刷新到redo log file中。

在这⾥,我们需要注意的是InnoDB的redo log的⼤⼩是固定的,分别有多个⽇志⽂件采⽤循环⽅式组成⼀个循环闭环,当写到结尾时,会回到开头循环写⽇志。我们可以通过参数innodblogfilesin_group和innodb_logfile_size配置⽇志⽂件数量和每个⽇志⽂件的⼤⼩。

Buffer Pool中更新的数据未刷新到磁盘中,该内存⻚我们称之为脏⻚。最终脏⻚的数据会刷新到磁盘中,将磁盘中的数据覆盖,这个过程与redo log不⼀定有关系。

只有当redo log⽇志满了的情况下,才会主动触发脏⻚刷新到磁盘,⽽脏⻚不仅只有redo log⽇志满了的情况才会刷新到磁盘,以下⼏种情况同样会触发脏⻚的刷新:

系统内存不⾜时,需要将⼀部分数据⻚淘汰掉,如果淘汰的是脏⻚,需要先将脏⻚同步到磁盘;
MySQL认为空闲的时间,这种情况没有性能问题;
MySQL正常关闭之前,会把所有的脏⻚刷⼊到磁盘,这种情况也没有性能问题。

在⽣产环境中,如果我们开启了慢SQL监控,你会发现偶尔会出现⼀些⽤时稍⻓的SQL。这是因为脏⻚在刷新到磁盘时可能会给数据库带来性能开销,导致数据库操作抖动。

39讲答疑课堂:MySQL中InnoDB的知识点串讲 - 图5

LRU淘汰策略

以上我们了解了InnoDB的更新和插⼊操作的具体实现原理,接下来我们再来了解下读的实现和优化⽅式。

InnoDB存储引擎是基于集合索引实现的数据存储,也就是除了索引列以及主键是存储在B +树之外,其它列数据也存储在B +
树的叶⼦节点中。⽽这⾥的索引⻚和数据⻚都会缓存在缓冲池中,在查询数据时,只要在缓冲池中存在该数据,InnoDB就不
⽤每次都去磁盘中读取⻚,从⽽提⾼数据库的查询性能。

虽然缓冲池是⼀个很⼤的内存区域,但由于存放了各种类型的数据,加上存储数据量之⼤,缓冲池⽆法将所有的数据都存储在
其中。因此,缓冲池需要通过LRU算法将最近且经常查询的数据缓存在其中,⽽不常查询的数据就淘汰出去。

InnoDB对LRU做了⼀些优化,我们熟悉的LRU算法通常是将最近查询的数据放到LRU列表的⾸部,⽽InnoDB则是将数据放在
⼀个midpoint位置,通常这个midpoint为列表⻓度的5/8。

这种策略主要是为了避免⼀些不常查询的操作突然将热点数据淘汰出去,⽽热点数据被再次查询时,需要再次从磁盘中获取, 从⽽影响数据库的查询性能。

如果我们的热点数据⽐较多,我们可以通过调整midpoint值来增加热点数据的存储量,从⽽降低热点数据的淘汰率。

总结

以上InnoDB的实现和运⾏原理到这⾥就介绍完了。回顾模块六,前三讲我主要介绍了数据库操作的性能优化,包括SQL语
句、事务以及索引的优化,接下来我⼜讲到了数据库表优化,包括表设计、分表分库的实现等等,最后我还介绍了⼀些数据库参数的调优。

总的来讲,作为开发⼯程师,我们应该掌握数据库⼏个⼤的知识点,然后再深⼊到数据库内部实现的细节,这样才能避免经常写出⼀些具有性能问题的SQL,培养调优数据库性能的能⼒。

这⼀讲的内容就到这⾥,相对基础,不熟悉的同学抓紧补补课,如有疑问,欢迎留⾔讨论。也欢迎你点击“请朋友读”,把今天的内容分享给身边的朋友,邀请他⼀起学习。
39讲答疑课堂:MySQL中InnoDB的知识点串讲 - 图6

  1. 精选留⾔

39讲答疑课堂:MySQL中InnoDB的知识点串讲 - 图7JackJin
⽼师,本讲的缓存池是基于引擎层的缓存吗?与server层的缓存有什么不同?
每个⻚存放的⾏记录也是有硬性定义的,最多允许存放 16KB/2-200⾏,即7992⾏,是怎么计算来的?
2019-08-22 17:41
作者回复
对的,我们之前讨论过server层的cache,主要是是根据我们查询SQL的hash值作为key,查询结果作为value值保存在cache层
。⽽存储引擎层的缓冲池,缓冲了很多类型的数据,例如索引⻚、数据⻚等。

如果⼀张表的数据经常被新增、更新、删除,则导致Cache层的失效率⾮常⾼,从⽽导致频繁清除已缓存的数据,server层的c
ache可能会增加数据库的性能,在MySQL8.0已经移除该功能,建议我们在业务层或数据库中间代理层来缓存查询的结果。例如mybatis和hibernate中利⽤⼀级缓存来缓存频繁查询的数据。
2019-08-24 09:47

39讲答疑课堂:MySQL中InnoDB的知识点串讲 - 图8张学磊
⽼师,客户端读取数据时,如果数据存在于缓冲池中,客户端就会直接读取缓冲池中的数据,否则将磁盘中的数据加载到缓冲池,再从缓冲池中读取,客户端始终和缓冲池交互,准确的说是不是应该这样?
另外有⼀处编辑错误,InnoDB 存储引擎是⾯向列的(row-oriented),应该写成⾏。
2019-08-22 08:18
39讲答疑课堂:MySQL中InnoDB的知识点串讲 - 图9许童童
⽼师讲得很好,这个知识串讲很不错,跟着⽼师⼀起精进。
2019-08-22 14:58
作者回复
⼀起进步
2019-08-24 09:38