极客时间已完结课程限时免费阅读

40 | 答疑课堂:MySQL中InnoDB的知识点串讲

40 | 答疑课堂:MySQL中InnoDB的知识点串讲-极客时间

40 | 答疑课堂:MySQL中InnoDB的知识点串讲

讲述:李良

时长10:30大小9.60M

你好,我是刘超。
模块六有关数据库调优的内容到本周也正式结束了,今天我们一起串下 MySQL 中 InnoDB 的知识点。InnoDB 存储引擎作为我们最常用到的存储引擎之一,充分熟悉它的的实现和运行原理,有助于我们更好地创建和维护数据库表。

InnoDB 体系架构

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

1. 内存池

我们知道,如果客户端从数据库中读取数据是直接从磁盘读取的话,无疑会带来一定的性能瓶颈,缓冲池的作用就是提高整个数据库的读写性能。
客户端读取数据时,如果数据存在于缓冲池中,客户端就会直接读取缓冲池中的数据,否则再去磁盘中读取;对于数据库中的修改数据,首先是修改在缓冲池中的数据,然后再通过 Master Thread 线程刷新到磁盘上。
理论上来说,缓冲池的内存越大越好。我们在第 38 讲中详细讲过了缓冲池的大小配置方式以及调优。
缓冲池中不仅缓存索引页和数据页,还包括了 undo 页,插入缓存、自适应哈希索引以及 InnoDB 的锁信息等等。
InnoDB 允许多个缓冲池实例,从而减少数据库内部资源的竞争,增强数据库的并发处理能力,第 38 讲还讲到了缓冲池实例的配置以及调优。
InnoDB 存储引擎会先将重做日志信息放入到缓冲区中,然后再刷新到重做日志文件中。

2. 后台线程

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

3. 存储文件

在 MySQL 中建立一张表都会生成一个.frm 文件,该文件是用来保存每个表的元数据信息的,主要包含表结构定义。
在 InnoDB 中,存储数据都是按表空间进行存放的,默认为共享表空间,存储的文件即为共享表空间文件(ibdata1)。若设置了参数 innodb_file_per_table 为 1,则会将存储的数据、索引等信息单独存储在一个独占表空间,因此也会产生一个独占表空间文件(ibd)。如果你对共享表空间和独占表空间的理解还不够透彻,接下来我会详解。
而日志文件则主要是重做日志文件,主要记录事务产生的重做日志,保证事务的一致性。

InnoDB 逻辑存储结构

InnoDB 逻辑存储结构分为表空间(Tablespace)、段 (Segment)、区 (Extent)、页 Page) 以及行 (row)。

1. 表空间(Tablespace)

InnoDB 提供了两种表空间存储数据的方式,一种是共享表空间,一种是独占表空间。 InnoDB 默认会将其所有的表数据存储在一个共享表空间中,即 ibdata1。
我们可以通过设置 innodb_file_per_table 参数为 1(1 代表独占方式)开启独占表空间模式。开启之后,每个表都有自己独立的表空间物理文件,所有的数据以及索引都会存储在该文件中,这样方便备份以及恢复数据。

2. 段 (Segment)

表空间是由各个段组成的,段一般分为数据段、索引段和回滚段等。我们知道,InnoDB 默认是基于 B + 树实现的数据存储。
这里的索引段则是指的 B + 树的非叶子节点,而数据段则是 B + 树的叶子节点。而回滚段则指的是回滚数据,之前我们在讲事务隔离的时候就介绍到了 MVCC 利用了回滚段实现了多版本查询数据。

3. 区 (Extent) / 页(Page)

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

4. 行(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 的大小是固定的,分别有多个日志文件采用循环方式组成一个循环闭环,当写到结尾时,会回到开头循环写日志。我们可以通过参数 innodb_log_files_in_group 和 innodb_log_file_size 配置日志文件数量和每个日志文件的大小。
Buffer Pool 中更新的数据未刷新到磁盘中,该内存页我们称之为脏页。最终脏页的数据会刷新到磁盘中,将磁盘中的数据覆盖,这个过程与 redo log 不一定有关系。
只有当 redo log 日志满了的情况下,才会主动触发脏页刷新到磁盘,而脏页不仅只有 redo log 日志满了的情况才会刷新到磁盘,以下几种情况同样会触发脏页的刷新:
系统内存不足时,需要将一部分数据页淘汰掉,如果淘汰的是脏页,需要先将脏页同步到磁盘;
MySQL 认为空闲的时间,这种情况没有性能问题;
MySQL 正常关闭之前,会把所有的脏页刷入到磁盘,这种情况也没有性能问题。
在生产环境中,如果我们开启了慢 SQL 监控,你会发现偶尔会出现一些用时稍长的 SQL。这是因为脏页在刷新到磁盘时可能会给数据库带来性能开销,导致数据库操作抖动。

LRU 淘汰策略

以上我们了解了 InnoDB 的更新和插入操作的具体实现原理,接下来我们再来了解下读的实现和优化方式。
InnoDB 存储引擎是基于集合索引实现的数据存储,也就是除了索引列以及主键是存储在 B + 树之外,其它列数据也存储在 B + 树的叶子节点中。而这里的索引页和数据页都会缓存在缓冲池中,在查询数据时,只要在缓冲池中存在该数据,InnoDB 就不用每次都去磁盘中读取页,从而提高数据库的查询性能。
虽然缓冲池是一个很大的内存区域,但由于存放了各种类型的数据,加上存储数据量之大,缓冲池无法将所有的数据都存储在其中。因此,缓冲池需要通过 LRU 算法将最近且经常查询的数据缓存在其中,而不常查询的数据就淘汰出去。
InnoDB 对 LRU 做了一些优化,我们熟悉的 LRU 算法通常是将最近查询的数据放到 LRU 列表的首部,而 InnoDB 则是将数据放在一个 midpoint 位置,通常这个 midpoint 为列表长度的 5/8。
这种策略主要是为了避免一些不常查询的操作突然将热点数据淘汰出去,而热点数据被再次查询时,需要再次从磁盘中获取,从而影响数据库的查询性能。
如果我们的热点数据比较多,我们可以通过调整 midpoint 值来增加热点数据的存储量,从而降低热点数据的淘汰率。

总结

以上 InnoDB 的实现和运行原理到这里就介绍完了。回顾模块六,前三讲我主要介绍了数据库操作的性能优化,包括 SQL 语句、事务以及索引的优化,接下来我又讲到了数据库表优化,包括表设计、分表分库的实现等等,最后我还介绍了一些数据库参数的调优。
总的来讲,作为开发工程师,我们应该掌握数据库几个大的知识点,然后再深入到数据库内部实现的细节,这样才能避免经常写出一些具有性能问题的 SQL,培养调优数据库性能的能力。
这一讲的内容就到这里,相对基础,不熟悉的同学抓紧补补课,如有疑问,欢迎留言讨论。也欢迎你点击“请朋友读”,把今天的内容分享给身边的朋友,邀请他一起学习。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 9

提建议

上一篇
39 | 数据库参数设置优化,失之毫厘差之千里
下一篇
41 | 如何设计更优的分布式锁?
unpreview
 写留言

精选留言(13)

  • 2019-09-15
    到此刻Java性能调优实战算是从头到尾的学习了一遍,首先,为老师的敬业态度点赞,跟着老师也学习到了许多性能调优的方法和技巧。之前也看过书,加入目前的公司后加班比较严重,看书的效率也不高了,从去年在极客时间学习后就一发不可收拾买了几十个专栏,现在还有许多未开始学习,不过买专栏有种令人安心的感觉,每天也在不断的学习,看的多了发现不同的专栏重复的内容也多了,当然,看不完了还继续买,我觉得买专栏的钱其实相对于学习到的知识来讲是非常值得的。哪怕只学习一遍,有些专栏值得一刷再刷,毕竟写专栏的老师水货少,极客时间选择讲师的专业性,也是我不断在这里付费的一个原因。我发现没有九阳神功真是学什么都慢,在计算机的世界里计算机组成原理/操作系统原理/编译原理/计算机网络原理/计算机数据库原理/数据结构与算法这些就是九阳神功,如果这些精通,什么这个那个招式全都一学就会且速度快印象深。 计算机界所有的这个理论那个原理基本都是围绕性能和容量在做文章,都想要更快的速度更大的存储空间,在升一层就是为了和时间赛跑开拓更为广阔的空间,快能占据许多的先机,不管什么行业快都具有优势。在极客时间我感觉增长见闻的效率还是挺高的,不过不看书还是不行,深度细致度系统性这些知识体验还是不能和书媲美。不学习是不可能的,人生如逆水行舟不进则退,其实学习的慢也会渐渐失去竞争优势。好奇老师这么多知识积累了多久?都是怎么积累的?除了坚持还有什么别巧妙嘛? 我的同事中有一位非常拼命,两年学习的东西顶别人四五年,住在公司附近几乎每天都疯狂加班学习,现在也非常厉害从工资上来说已经秒杀了许多工作七八年的人。先去阿里再去拼多多,真是太拼了,生活就是上班加班学习。其实我也想这么拼,我也想年入百万,不过即使时间付出的差不多,人的脑回沟及过往的受教育经历特别是思维方式的训练加英语的加持也会导致非常大的差异。 嗯,感慨有点多,继续加油吧!保持好学习的节奏,买的专栏或书,如果不看不学,还不如买几卷卫生纸。 再次感谢老师的分享,我相信有朝一日我也会像老师一样牛逼!
    展开

    作者回复: 👍加油

    共 4 条评论
    27
  • 拒绝
    2019-08-22
    老师,本讲的缓存池是基于引擎层的缓存吗?与server层的缓存有什么不同? 每个页存放的行记录也是有硬性定义的,最多允许存放 16KB/2-200行,即7992行,是怎么计算来的?

    作者回复: 对的,我们之前讨论过server层的cache,主要是是根据我们查询SQL的hash值作为key,查询结果作为value值保存在cache层。而存储引擎层的缓冲池,缓冲了很多类型的数据,例如索引页、数据页等。 如果一张表的数据经常被新增、更新、删除,则导致Cache层的失效率非常高,从而导致频繁清除已缓存的数据,server层的cache可能会增加数据库的性能,在MySQL8.0已经移除该功能,建议我们在业务层或数据库中间代理层来缓存查询的结果。例如mybatis和hibernate中利用一级缓存来缓存频繁查询的数据。

    共 3 条评论
    6
  • 平凡之路
    2019-12-04
    老师,您好,我一直有个疑问,如果数据库设置最大连接数为1000,如果像电商系统这样的网站,同时有10000个人访问登录,那不是有9000人都要等待?电商系统是用的其他数据库存储系统吗?

    作者回复: 如果是同一时刻都进入到数据库,其他9000人肯定是在等待状态。数据库操作都是毫秒级的,1000人使用完之后,空闲的连接会被释放出来,所以哪怕是9000人等待,后面也会在一秒之内完成。

    共 4 条评论
    4
  • 张学磊
    2019-08-22
    老师,客户端读取数据时,如果数据存在于缓冲池中,客户端就会直接读取缓冲池中的数据,否则将磁盘中的数据加载到缓冲池,再从缓冲池中读取,客户端始终和缓冲池交互,准确的说是不是应该这样? 另外有一处编辑错误,InnoDB 存储引擎是面向列的(row-oriented),应该写成行。

    作者回复: 优先与缓冲池交互。问题已改正,谢谢指正。

    2
  • 楞伽山人
    2021-12-29
    老师 您好 undo log是解决ACID中的A吧 不是C吧
    共 2 条评论
    1
  • 天天向上
    2020-02-01
    老师在:MySQL8.0取消cache 那岂不是每次查询都要访问磁盘 这样不合理吧 的回复中回答:还有一层缓冲池。但是缓冲池命中的概率很低很低吧。

    作者回复: 如果频繁访问且变更非常少的数据,建议在业务层进行缓存。

    1
  • 张德
    2019-09-03
    老师 MySQL8.0取消cache 那岂不是每次查询都要访问磁盘 这样不合理吧 还是我理解错了

    作者回复: 不会的,还有一层缓冲池

    共 2 条评论
    1
  • FiRerOUNd
    2022-08-10 来自北京
    我理解的innodb没有实现redolog,这个是mysql服务器层实现的,所有存储引擎共同这个文件。
  • ETLshow
    2022-04-10
    性能调优还是要多刷几遍
  • ty_young
    2021-02-14
    “Pager Cleaner Thread 是新引入的一个用于协助 Master Thread 刷新脏页到磁盘的线程”,老师好,请问这个脏页值的是什么
  • 旅途
    2020-09-13
    Buffer Pool 中更新的数据未刷新到磁盘中,该内存页我们称之为脏页 问下老师 这句话的意思是更新的数据未成功刷新到磁盘还是在内存中未刷新到磁盘
    共 1 条评论
  • ty_young
    2019-10-10
    老师您好,请问Master Thread回收的undo页和Purge Thread回收的undo log是一回事么

    作者回复: 在InnoDB 1.1之前,这个动作在master thread中进行,在1.1之后,独立到了purge thread中进行,所以是一回事。

  • 许童童
    2019-08-22
    老师讲得很好,这个知识串讲很不错,跟着老师一起精进。

    作者回复: 一起进步