innodb为什么写入数据快_InnoDB存储引擎详解(一)

7c3d64a9518d59a507d79f0e6b9c52aa.png

每个人看架构图的理解和反应都会不一样,熟知原理的人,看到架构图的每个细节都会加以思考,所以对他来说,架构图就是连贯他所学知识点的最好工具,而学习每个知识点的结晶也是一张架构图。而对于刚入门的人来说,一看架构图就头疼,这样反而让他产生一种抗拒的心态,也懒得去理解架构图,所以是很难去吸收知识的,成长的步伐也会变慢。我们要掌握一门技术,就要做到一看架构图,就明白每个组件的作用和原理,这样才能说掌握了这门技术(还不能说熟练)

MySQL的架构图如下

19b7c5612c5bbae16bc4128c50355889.png

其中上半部分请看这篇文章

船长:【MySQL那些事】一条sql的寻宝之旅​zhuanlan.zhihu.com
903c942a29560d379833f743cd1ae4f3.png

本文主要是针对下半部分InnoDB存储引擎的详解

InnoDB内部主要分为后台线程和内存池两大块

内存池

bb023b7793d759884259ceb6bd5187a8.png

1、缓冲池

InnoDB是基于磁盘存储的引擎,其真正的数据最终都是保存在文件中的,并且以页为最小存储单位,学过计算机组成原理的同学都知道,CPU的读写速度和磁盘的速度是不能比拟的,所以直接从磁盘取数据无疑是降低了数据库的性能,所以InnoDB提供了一块内存来弥补磁盘读写速度慢的缺陷,这块内存就是缓冲池。

工作方式:
数据库读取页时,首先去缓冲池中查找该页是否存在,若不存在再去磁盘查找是否存在,若存在则将页放在缓冲池中,以便下次查找时,可以直接取出来。
数据库修改页时,首先修改缓冲池中的页,然后再以一定的频率刷新到磁盘中。并不是每次修改都去修改磁盘,那样的话性能还是很低,而是通过一种称为Checkpoint的机制将数据刷新回磁盘。已在缓冲池修改而还未更新到磁盘的页称之为 脏页

由此可见,缓冲池的大小直接影响着数据库的整体性能,缓冲池可以使用参数innodb_buffer_pool_size来配置,在32位系统中,最多只能设置为3GB,而在64位系统中则不限。

a8cff8d34dcbd9fc42f24752c61b0bf2.png

从InnoDB 1.0版本开始,允许有多个缓冲池实例。每个页根据hashCode分配到不同的缓冲池实例中,这样做的好处是减少数据库内部的资源竞争,增加并发能力,可以通过参数innodb_buffer_pool_instances进行配置,其默认值是1

e138f3b29060b2d42c81e1f7e185187f.png

2、重做日志缓冲

重做日志(redo log)是数据库的重要组成,他保证了数据库宕机并重启后数据恢复的正确性。重做日志共有两个文件

b5b816b6ea96a7c7d14e7bc856a1c754.png

每个文件的大小一致且循环写入,也就是说先写ib_logfile0,写满之后再写ib_logfile1,又写满后,继续写ib_logfile0。

redo log:为了持久化数据,当内存中的数据还没写入到磁盘而宕机时,会读取该日志持久化数据到磁盘
undo log:为了保证原子性,事务的操作都会记录一条相反的sql到该日志,出现错误就会根据该文件恢复事务之前的数据
binlog:为了复制和恢复数据,mysql重启可以通过该日志恢复

当数据库修改页时,会将修改信息放入重做日志缓冲区,然后按一定频率(默认每秒一次)写入到重做日志文件,重做日志缓冲区一般不需要设置很大,只要保证每秒产生的事务量在这个缓冲区大小内即可,缓冲区大小可以通过参数innodb_log_buffer_size配置,默认为8MB

897a78ab7419fd0cbe133f59579fe11a.png

下面三种情况下会将缓冲区内容写入到重做日志中

1、主线程Master Thread每秒会写入一次
2、每个事务提交时会写入一次
3、当重做日志缓冲池剩余空间小于50%时,会写入一次

3、额外的内存池

在InnoDB存储引擎中,对内存的管理是通过一种称之为内存堆的方式进行的,在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中申请。

后台线程

1、主线程Master Thread

主线程负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲、undo页的回收等等

Master Thread具有最高的线程优先级别,其内部由多个循环组成:主循环Loop、后台循环Background Loop、刷新循环Flush Loop、暂停循环Suspend Loop。Master Thread会根据数据库的状态在这几个循环中切换。

6f60298cc88ca0c56738af96892e323f.png

主循环Loop有两大操作——每秒和每10秒,并且是通过线程的sleep实现循环的,可见当负载很大时,会出现延迟的情况,所以并不是很精确的每秒和每10秒操作

每秒的操作如下:

日志缓冲写入到磁盘:即使事务还未提交,都会每秒将重做日志缓冲区的内容写入到重做日志文件
合并插入缓冲:先判断前一秒发生的IO次数是否小于5,如果小于5则认为IO压力很小,可以进行合并插入缓冲的操作
至多写入100个缓冲池中的脏页到磁盘:InnoDb会判断当前缓冲池中脏页的比例(buf_get_modified_ratio_pct)是否超过了参数 innodb_max_dirty_pages_pct(默认为90,代表90%),如果超过则认为需要做磁盘同步操作,就将100个脏页写入磁盘
如果当前没有用户活动,则切换到background loop

注意:插入缓冲是指插入数据前,非聚集索引会先缓存到缓冲池的插入缓冲区,然后按一定频率刷新到数据库,并非每插入一条,就更新一次索引树,这样效率同样很低,关于插入缓冲的更多知识会在下节详解。

每10秒的操作如下:

写入100个脏页到磁盘:InnoDB会先判断前10秒内的IO操作次数是否小于200,是则认为磁盘IO压力较小,可以将100个脏页写到磁盘。
合并至多5个插入缓冲
日志缓冲写入到磁盘
删除无用的undo页
写入100个或10个脏页到磁盘:如果缓冲池中的脏页比例(buf_get_modified_ratio_pct)超过70%,会刷新100个脏页,否则刷新10个脏页

742b1c8366d0d9b9eae46a9245e20b18.png

当数据库空闲或者数据库关闭时,就会切换到后台循环Background Loop,其操作有:

删除无用的undo页
合并20个插入缓冲
跳回到主循环
跳到刷新循环flush loop

刷新循环Flush Loop只做了一件事:刷新页到缓冲池

若刷新循环也没事情做了,InnoDB就会切换到暂停循环Suspend Loop,此时Master Thread就挂起并等待事件的发生

以上是InnoDB 1.0版本之前的操作,可以发现主线程中最多一次刷新100个脏页到磁盘,合并20个插入缓冲,如果在数据量密集的项目中,每秒产生的脏页可能远远大于100,这就会造成主线程忙不过来而变得很慢。同时发生宕机并恢复时,因为很多数据没有写到磁盘,所以恢复时间会大大延长。故InnoDB 1.2版本对主线程Master Thread进行了优化。

InnoDB 1.2通过提供参数innodb_io_capacity的配置来解决该问题,其值默认为200

从缓冲区刷新脏页时,刷新脏页数量为innodb_io_capacity的值
在合并插入缓冲时,合并数量为innodb_io_capacity的5%,即默认为40

如果固态硬盘SSD等高速硬盘,可以通过提高该参数的值来提高主线程的处理能力

另外上面说到,每秒循环中在脏页比例大于参数innodb_max_dirty_pages_pct的值时(默认90%),才会刷新100个脏页到磁盘,比例90%明显是太大了,因为假如内存很大,或者服务器压力很大,此时刷新90%那么多的数据,速度必然变慢,所以该参数的默认值修改为75了

InnoDB 1.2还引入了一个参数innodb_adaptive_flushing(自适应刷新),本来缓冲池中的脏页比例大于90%时才会刷新(现在是75%了),但是现在会通过计算重做日志(redo log)的速度来决定最合适的刷新脏页数量,当这个数量大于自适应刷新的配置时,也会进行一次刷新,可见脏页比例小于75%时,也会通过自适应刷新处理一定的脏页。

同时对于刷新脏页的操作,Master Thread分离到一个单独的线程Page Cleaner Thread(脏页清除线程)中进行,从而减轻了主线程的工作,进一步提高系统的并发性能。

2、读写线程IO Thread

在InnoDB中大量使用了异步IO(Async IO)来处理读写请求,而IO Thread就是负责处理这些请求的回调。

在InnoDB 1.0版本 之前共有4个读写线程:write、read、insert buffer和log IO。

在InnoDB 1.0之后write和read分别增大到4个,可以使用innodb_read_io_threadsinnodb_write_io_threads参数进行设置

c996aead39814178f6831530ac2f4a31.png

3、回收线程Purge Thread

当事务成功提交后,事务所关联的undo log已经不再需要,故需要使用回收线程去回收所分配的undo页。

回收线程的数量默认为1个,可以通过参数innodb_purge_threads进行配置

4、脏页清除线程Page Cleaner Thread

这是从主线程中分离出来,专门用于刷新脏页到磁盘的线程,其目的是减轻Master Thread的工作及对于用户查询线程的阻塞,进一步提高性能。

cefbfbd10116299ef5e6da59d3ed0e28.png

关注公众号【全栈船长】获取更多资料

65f6d2a7673ecfeec19becb5b605057d.png
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值