事情是这样的,在某乎的邀请回答中看到了这个问题:
–
然后当时我没多想就啪一下写下来这样的答案:
这个其实要通过 MySQL 后台线程来刷的,在 Buffer Pool 中被修改的过的 Page(页)都会被标记成脏页,放到一个链表(Flush 链表)里。
然后 MySQL 通过启动后台线程,在满足条件时将 Flush 链表中的脏页刷入磁盘。
满足的条件是: 脏页的数量 达到了 Buffer Pool 中页数量的 **10% ,当然 10% 这个值是可变的,通过配置项 innodb_max_dirty_pages_pct_lwm 来配置的,其默认值为 10%,并且这个值也必须小于另一个配置 innodb_max_dirty_pages_pct 的值( 90%**)。
至于启多少个线程,则是由另一个变量 innodb_page_cleaners 来控制的,默认是 4.一般都不会去改这个。
大概就是这样。
但是,后面有兄弟在下面说:”我唔知你喺讲乜“。
后面我回过头去看,当时写的确实有点过于跳跃了,过一段时间再去看有些不是那么连贯,打算重新把这个事情讲清楚。
1. 表数据
我们这篇「短文」讨论的是【MySQL 表数据多久刷一次盘】,从这个标题中我们可以分裂成两个问题:
刷什么到磁盘
什么时候刷到磁盘
我们分开来讨论。
2. 刷什么到磁盘
看上去有点废话,肯定是将数据刷入磁盘。所以我们更多需要讨论的是【数据是以什么样的形式被刷入磁盘】。
答案是页
对页不太了解的可以去看看之前写的文章: MySQL 页完全指南——浅入深出页的原理
在 Inn
oDB 中, 页 是数据被管理的最小的单位。当使用 InnoDB 作为存储引擎的 MySQL 运行时,表中一行一行的数据会被组织在一页一页当中,放在 Buffer Pool 中。
Buffer Pool 可以看另一篇: 详细了解 InnoDB 内存结构及其原理
这一页一页的数据,就存放在 Buffer Pool 中。当 DML 语句(也就是 CRUD)语句对表数据进行了变更之后,数据所在的那一页就会被标记为 脏页 。
InnoDB 会用一个叫【Flush 链表】的结构来存放这些脏页,凡是被放进该链表的页都代表需要 刷入磁盘 ,但不是立即刷入。
和 InnoDB 的其他日志例如 Redo Log 一样,这些日志都是有自己的 刷盘策略 。例如 Redo Log,其刷盘策略可以用下图来表示:
参数为0,Redo Log 会每隔一秒,写入并且刷入磁盘。
参数为1,Redo Log 会在每次事务提交之后刷入磁盘
参数为2,每次事务提交,都会写到 OS 缓存中去,然后每隔一秒将 OS 缓存中的数据刷入磁盘
而 Flush 链表也有自己的策略。
3. 什么时候刷到磁盘
接上节,策略就是: 脏页的数量 达到了 Buffer Pool 中页数量的 **10%**,就会触发将 Flush 链表中的脏页刷入磁盘。举个例子,Buffer Pool 中总共有 100 张页,脏页如果达到了 10 页就会启动后台线程,触发刷盘。
当然,【10%】这个数值是可配置的,通过 MySQL 配置项 innodb_max_dirty_pages_pct_lwm 可以进行调整,只是默认值是 10%。但是我们调整的值不能超过某个最大值,这个最大值由 innodb_max_dirty_pages_pct 来指定,默认值为 90%。
换句话说,默认情况,刷盘阈值是 10%,如果需要自定义,则最大值不能超过 90%。
4. 谁来负责刷盘
上个小节已经说过了,会启动线程来专门做这个事情,这个没有什么疑问。我们需要关注的是会启动多少个线程来做这个事。
答案是 4 个,我们也可以通过配置项 innodb_page_cleaners 来更改,但一般都不会去改这个值。
关于这个点就聊到这。
InnoDB在处理更新语句时,先写内存再写redo log,并不会立即将数据页的更新落地到磁盘(WAL机制),这就会产生升内存数据页和磁盘数据页的数据不一致的情况,这种数据不一致的数据页称为 脏页 ,当脏页写入到磁盘(这个 *** 作称为flush)后,数据一致后称为干净页。
第3种是系统空闲不会有性能问题,第4种是要关闭了不考虑性能问题。第1和2的情况flush脏页会产生系统性能问题。
此时整个系统不能再更新了,更新数会降为0,所以这种情况要尽量避免。
InnoDB缓冲池(buffer pool)中的内存页有三种状态:
当一个SQL语句要淘汰的脏页数量太多,会导致语句执行的响应时间显著边长。
InnoDB为了避免出现上述两种情况,需要有控制脏页比例的策略,控制的主要参考因素就是:脏页比例和redo log写盘速度。
需要告诉InnoDB的磁盘读写能力(IOPS)让引擎全力flush脏页,磁盘的IOPS可以通过fio工具测试。
如果 innodb_io_capacity 参数设置的不合理,比如远远低于磁盘实际的IOPS,InnoDB会认为IO性能低,刷脏页速度会很慢,甚至低于脏页的生成速度,导致脏页累计影响查询和更新性能。
为了兼顾正常的业务请求,InnoDB引擎控制按照磁盘IOPS的百分比来刷脏页,具体流程如下:
脏页比例计算:
Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total
SQL语句如下:
在准备flush一个脏页时,如果相邻的数据页也是脏页,会把这个脏页一起flush,而且对这个新的脏页还可能有相邻的脏页导致连锁flush。
InnoDB使用 innodb_flush_neighbors 参数控制这个行为,值为1会产生上述连锁flush的情况,值为0则不会找相邻页。
找相邻页flush的机制虽然可以减少很多随机IO,但会增加一次flush时间,导致flush时的SQL语句执行时间变慢。
现在基本都使用的SSD这种IOPS比较高的硬盘,建议将 innodb_flush_neighbors 参数设为0,提高flush的速度。
flush会占用IO资源影响了正在执行的SQL语句,本来正常情况下执行很快的一条语句,突然耗时大大增加,造成业务抖动。要尽量避免这种情况,需要合理的设置 innodb_io_capacity 的值,并且多关注脏页比例,不要让脏页比例经常接近75%。
【极客时间】 MySQL实战45讲:第12节
当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”。内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”。
不论是脏页还是干净页,都在内存中。
平时很快的更新 *** 作,都是在写内存和日志。
一条 SQL 语句,正常执行的时候特别快,但是有时也不知道怎么回事,它就会变得特别慢。
那这时候可能就是在刷脏页到磁盘中了~ flush
(1) InnoDB的redo log写满了。这时候系统会停止所有的更新 *** 作,然后让日志可以继续写。
把这部分数据日志都flush到磁盘上面。
(2) 也可能是系统内存不足,需要新的内存页,那么就淘汰一些内存页,空出来的给别的数据页使用。
先把脏页写到磁盘。
PS:使用内存是为了效率更好,
因为如果内存存在数据页,那么数据就一定正确,直接返回;
如果内存没有数据,才需要去磁盘中取,读入到内存,返回;
(3) MySQL 认为系统“空闲”的时候,反正闲着也是闲着hh
反正有机会就刷点数据
(4)MySQL 正常关闭。这时候,MySQL 会把内存的脏页都 flush 到磁盘上,这样下次 MySQL 启动的时候,就可以直接从磁盘上读数据,启动速度会很快。
3.1 如果是redo log写满了
尽量避免的。因为出现这种情况的时候,整个系统就不能再接受更新了,所有的更新都必须堵住。更新数为 0。
3.2 内存不够用了
常态,很正常。
3.3 buffer pool
因为innodb用的是buffer pool 管理内存,缓冲池中的内存页有三种状态:第一种是还没有使用的;第二种是使用了并且是干净页;第三种是使用了并且是脏页。
Innodb 的内存策略是尽量使用内存。
我觉得知道一下就好,这个脏页刷的快不快跟磁盘的能力有关。
可以通过innodb_io_capacity 这个参数设置磁盘能力。
InnoDB 的刷盘速度就是要参考这两个因素:一个是脏页比例,一个是 redo log 写盘速度。
平时要多关注脏页比例,不要让它经常接近 75%。
INNODB刷脏页,如果发现旁边也是脏页,那么会连带着一起刷掉。
所以可能会很慢,如果你的查询正好要先flush一个脏页的话。
在 InnoDB 中,innodb_flush_neighbors 参数就是用来控制这个行为的,值为 1 的时候会有上述的“连坐”机制,值为 0 时表示不找邻居,自己刷自己的。
找“邻居”这个优化在机械硬盘时代是很有意义的,可以减少很多随机 IO。机械硬盘的随机 IOPS 一般只有几百。
但是SSD 的IO很高,所以可以不用非要有刷写邻居的 *** 作,可以加快响应。
在 MySQL 8.0 中,innodb_flush_neighbors 参数的默认值已经是 0 了。
对比这个LSN跟checkpoint 的LSN,比checkpoint小的一定是干净页
也就是如果内存中比redolog的头部小,那么就是干净页
每个数据页有LSN,重做日志有LSN,checkpoint有LSN。
占用8字节,LSN主要用于发生crash时对数据进行recovery,LSN是一个一直递增的整型数字,表示事务写入到日志的字节总量。
LSN不仅只存在于重做日志中,在每个数据页头部也会有对应的LSN号,该LSN记录当前页最后一次修改的LSN号,用于在recovery时对比重做日志LSN号决定是否对该页进行恢复数据。前面说的checkpoint也是有LSN号记录的,LSN号串联起一个事务开始到恢复的过程。
感谢: https://www.cnblogs.com/drizzle-xu/p/9713378.html
我感觉就是可以理解为是一个long类型的数字,可以根据这个来比较要不要刷写数据,以及是不是干净页面,在恢复数据要拿这个进行比较。
缓存区域,缓存数据和索引在内存中。
innodb使用了一些链表。
lru链表:用来存储内存中的缓存数据。
free链表:用来存放所有的空闲页,每次需要数据页存储数据时,就首先检测free中有没有空闲的页来分配。
flush链表:在内存中被修改但还没有刷新到磁盘的数据页列表,就是所谓的脏页列表,内存中的数据跟对应的磁盘上的数据不一致,属于该列表的页面同样存在于lru列表中,但反之未必。
将脏页flush到磁盘上是直接将脏页数据覆盖到对应磁盘上的数据
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)