浅析 InnoDB 存储引擎的工作流程源码分析

InnoDB

InnoDB 是由 Innobase Oy 公司开发,该存储引擎是第一个完整支持 ACID 事务的 MySQL 存储引擎。具有插入缓存、两次写、自适应哈希索引等关键特性,是一个高性能、高可用的存储引擎。

整体架构

InnoDB 有多个内存块,这些内存块组合在一起组成了一个大的内存池。而 InnoDB 的内存池中会有多个后台线程,这些后台线程负责刷新内存池中的数据,和将脏页(已修改的数据页)刷新到磁盘文件。关注、转发、评论头条号每天分享java知识,私信回复“555”赠送一些Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式资料

后台线程

默认情况下,InnoDB 存储引擎有 13 个后台线程:

一个 master 线程

一个锁监控线程

一个错误监控线程

十个 IO 线程

插入缓存线程

日志线程

读线程(默认 4 个)

写线程(默认 4 个)

下面是我本机上的十个 IO 线程

--------

FILE I/O

--------

I/O thread 0 state: waiting for i/o request (insert buffer thread)

I/O thread 1 state: waiting for i/o request (log thread)

I/O thread 2 state: waiting for i/o request (read thread)

I/O thread 3 state: waiting for i/o request (read thread)

I/O thread 4 state: waiting for i/o request (read thread)

I/O thread 5 state: waiting for i/o request (read thread)

I/O thread 6 state: waiting for i/o request (write thread)

I/O thread 7 state: waiting for i/o request (write thread)

I/O thread 8 state: waiting for i/o request (write thread)

I/O thread 9 state: waiting for i/o request (write thread)

Pending normal aio reads: 0 [0, 0, 0, 0] , aio writes: 0 [0, 0, 0, 0] ,

ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0

Pending flushes (fsync) log: 0; buffer pool: 0

540 OS file reads, 89 OS file writes, 7 OS fsyncs

0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s

内存池

InnoDB 存储引擎的内存池包含:缓冲池、日志缓存池、额外内存池。这些内存的大小分别由配置文件中的参数决定。其中占比最大的是缓冲池,里面包含了数据缓存页、索引、插入缓存、自适应哈希索引、锁信息和数据字典。InnoDB 会在读取数据库数据的时候,将数据缓存到缓冲池中,而在修改数据的时候,会先把缓冲池中的数据修改掉,一旦修改过的数据页就会被标记为脏页,而脏页则会被 master 线程按照一定的频率刷新到磁盘中。日志缓存则是缓存了redo-log 信息,然后再刷新到 redo-log 文件中。额外内存池则是在对一些数据结构本身分配内存时会从额外内存池中申请内存,当该区域内存不足则会到缓冲池中申请。

Master Thread

InnoDB 存储引擎的主要工作都在一个单独的 Master Thread 中完成,其内部由四个循环体构成:主循环( loop )、后台循环( background loop )、刷新循环( flush loop )、暂停循环( suspend loop )。具体工作流程如下图所示:

主循环

主要负责将缓冲池中的日志文件刷新到磁盘中、合并插入缓存、刷新缓冲池中的脏页数据到磁盘中、删除无用的 Undo 页、产生一个 checkpoint 。在主循环中会多次将脏页刷新到磁盘中,但是有一些刷新任务总会执行,有一些则根据参数来判断当前是否需要刷新。而这个参数 innodb_max_dirty_pages_pct 最大脏页比例是通过配置文件决定的,你可以根据实际情况来调整你自己的最大脏页比例,来达到最好的性能。

伪代码如下:

for (int i = 0; i<10; i++) {

thread_sleep(1)

do log buffer flush to disk

if ( last_one_second_ios < 5) {

do merge at most 5 insert buffer

}

if (buf_get_modified_ratio_pct > innodb_max_ditry_pages_pct) {

do buffer pool flush 100 dirty page

}

if (no user activity) {

goto background loop

}

}

if (last_ten_second_ios < 200) {

do buffer pool flush 100 dirty page

}

do merge at most 5 insert buffer

do log buffer flush to disk

do full pourge

if (buf_get_modifued_ratio_pct > 70%) {

do buffer pool flush 100 dirty page

} else {

buffer pool flush 10 dirty page

}

do fuzzy checkpoint

goto loop

后台循环

在后台循环中 InnoDB 会做这些事:删除无用的Undo页、合并插入缓存。如果当前 InnoDB 处于空闲状态,则跳转到刷新循环,否则跳转到主循环继续处理数据。

伪代码如下:

do full purge

do merge 20 insert buffer

if (not idle) {

goto loop

} else {

goto flush loop

}

刷新循环

一旦执行到刷新循环,InnoDB 会一直处理脏页数据,直到脏页数据达到最大脏页比例以下。这时候会跳转到暂停循环中(所有数据都处理完毕)。

伪代码如下:

flush loop:

do buffer pool flush 100 dirty page

if (buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct) {

goto flush loop

} else {

goto suspend loop

}

暂停循环

在本循环中,InnoDB会将 Master Thread 挂起,减少内存资源使用,一直处于 waiting 状态,等待事件来唤醒。一旦有新的事件过来,就跳转到主循环中。

伪代码如下:

suspend loop:

suspend_thread()

waiting event

goto loop;

由此可以看出,master 线程的最大的工作内容就是刷新脏页数据到磁盘了。这一步就是把缓存池中被修改的数据页同步到磁盘中。而脏页数据的刷新基本上都是由innodb_max_dirty_pages_pct 来控制的,所以当你的服务器处理能力比较强,给 InnoDB 分配的内存池比较大,这时候可能你的脏页数据会很难达到最大脏页比,这时候你的数据基本上都在缓冲池中,可能需要很长一段时间才会到数据库磁盘文件中,也就是脏页的刷新速度会很低(MySQL 5.1之前的版本默认是 90%,后面调整到 75%)。所以实际应用中可以根据自己内存和数据库的读写量来设置这个最大脏页比。对于一次刷新脏页数量的设置,在 InnoDB Plugin 中有一个参数 innodb_adaptive_flushing自适应刷新,InnoDB 会根据产生的重做日志速度来计算出当前最适合的刷新脏页数量。当然 InnoDB Plugin 中还有其它很多参数配置,合理利用这些配置可以极大的提升 InnoDB 存储引擎的性能。

关键特性

前面说到 InnoDB 的三大特性分别为:插入缓存、两次写、自适应哈希索引。下面就简单介绍下这三大特性。

插入缓存

当我插入一条数据,该数据只有一个 ID 索引(聚集索引:数据行的物理顺序与列值的逻辑顺序相同)的时候,并且 ID 是自增长的,这时候页中的行记录按照 ID 顺序存放,所以只需要在最新页插入数据即可。但是如果我的表有多个非聚集索引(该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同),在插入的时候非聚集索引的插入不再是顺序的,这时候要离散的访问非聚集索引页,导致插入性能变低。而插入缓存则在插入的时候判断缓冲池中是否存在当前非聚集索引,如果存在则直接插入,否则先插入到一个缓存区,然后再通过 Master Thread 来合并插入缓存。这样极大的提高了数据的写性能。

两次写

两次写是为了解决在将缓冲池中的脏页刷新到磁盘的过程中,操作系统出现故障,导致当前的脏页部分写失效的问题。通过两次写在下次恢复的时候,InnoDB 会根据两次写的结果来恢复数据。

原理:在刷新脏页的时候,不是直接把脏页数据刷新到磁盘,而是将脏页先写到一个大小为2M的内存缓存中,再将这个内存缓存数据同步到磁盘的共享表空间中。当全部都写到共享表空间后,再将数据刷新到磁盘中。这样如果发生了上面描述的情况,这时候数据会在共享表空间中有个备份,恢复的时候就可以使用共享表空间的数据。

如果有数据库集群的情况下,master数据库是一定要开启两次写的,为了保证数据可靠性。而从数据库可以通过参数 skip_innodb_doublewrite 来禁止两次写功能,来提高插入效率。

自适应哈希索引

InnoDB 会监控对表示的索引查找,如果发现可以通过对索引进行哈希来优化搜索。这时候会对当前的索引建立哈希索引。称之为自适应哈希索引( AHI )。可以通过参数innodb_adaptive_hash_index 来禁用或启用此特性。

小结

总体来说 InnoDB 的高性能体现在:插入数据的时候先保存在内存中,直接跟内存交互性能比较好,而且还有插入缓存优化,保证了高并发写操作。高可用则表现在两次写特性,保证了机器宕机或者出故障的时候数据不会丢失。这里只是简单介绍了一下 InnoDB 的工作流程和一些特性,当然 InnoDB 还有很多很多强大的功能,比如说事务、锁、索引、算法等等

 在此我向大家推荐一个架构学习交流群。交流学习群号:938837867 暗号:555 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备

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

推荐阅读更多精彩内容