您当前的位置:首页 > 电脑百科 > 数据库 > MYSQL

总结MySQL的所有buffer,一网打尽就这篇了!

时间:2023-03-29 14:05:02  来源:dbaplus社群  作者:

今天填一下之前的坑,盘一盘 MySQL 相关的 buffer。

我们来看一下官网的一张图:

图片这张图画的是 mysql innodb 的架构,从图中可以看到有很多 buffer,这篇我们就一个一个盘过去。

发车!(文档基于mysql8.0,以下描述的存储引擎基于 mysql innodb)

buffer pool

首先,我们来看下 buffer pool。

其实 buffer pool 就是内存中的一块缓冲池,用来缓存表和索引的数据。

我们都知道 mysql 的数据最终是存储在磁盘上的,但是如果读存数据都直接跟磁盘打交道的话,这速度就有点慢了。

所以 innodb 自己维护了一个 buffer pool,在读取数据的时候,会把数据加载到缓冲池中,这样下次再获取就不需要从磁盘读了,直接访问内存中的 buffer pool 即可。

包括修改也是一样,直接修改内存中的数据,然后到一定时机才会将这些脏数据刷到磁盘上。

看到这肯定有小伙伴有疑惑:直接就在内存中修改数据,假设服务器突然宕机了,这个修改不就丢了?

别怕,有个 redolog 的存在,它会持久化这些修改,恢复时可以读取 redolog 来还原数据,这个我们后面的文章再详盘,今天的主角是 buffer 哈。

回到 buffer pool,其实缓冲池维护的是页数据,也就是说,即使你只想从磁盘中获取一条数据,但是 innodb 也会加载一页的数据到缓冲池中,一页默认是 16k。

当然,缓冲池的大小是有限的。按照 mysql 官网所说,在专用服务器上,通常会分配给缓冲池高达 80% 的物理内存,不管分配多少,反正内存大小正常来说肯定不会比磁盘大。

也就是说内存放不下全部的数据库数据,那说明缓冲池需要有淘汰机制,淘汰那些不常被访问的数据页。

按照这个需求,我们很容易想到 LRU 机制,最近最少使用的页面将被淘汰,即维护一个链表,被访问的页面移动到头部,新加的页面也加到头部,同时根据内存使用情况淘汰尾部的页面。

通过这样一个机制来维持内存且尽量让最近访问的数据留在内存中。

看起来这个想法不错,但 innodb 的实现并不是朴素的 LRU,而是一种变型的 LRU。

从图中我们可以看出 buffer pool 分为了老年代(old sublist)和新生代(new sublist)。

老年代默认占 3/8,当然,可以通过 innodb_old_blocks_pct 参数来调整比例。

当有新页面加入 buffer pool 时,插入的位置是老年代的头部,同时新页面在 1s 内再次被访问的话,不会移到新生代,等 1s 后,如果该页面再次被访问才会被移动到新生代。

这和我们正常了解的 LRU 不太一样,正常了解的 LRU 实现是新页面插入到头部,且老页面只要被访问到就会被移动到头部,这样保证最近访问的数据都留存在头部,淘汰的只会是尾部的数据。

那为什么要实现这样改造的 LRU 呢?

innodb 有预读机制,简单理解就是读取连续的多个页面后,innodb 认为后面的数据也会被读取,于是异步将这些数据载入 buffer pool 中,但是这只是一个预判,也就是说预读的页面不一定会被访问。所以如果直接将新页面都加到新生代,可能会污染热点数据,但是如果新页面是加到老年代头部,就没有这个问题。

同时大量数据的访问,例如不带 where 条件的 select 或者 mysqldump 的操作等,都会导致同等数量的数据页被淘汰,如果简单加到新生代的话,可能会一次性把大量热点数据淘汰了,所以新页面加到老年代头部就没这个问题。

那 1s 机制是为了什么呢?

这个机制其实也是为了处理大量数据访问的情况,因为基本上大数据扫描之后,可能立马又再次访问,正常这时候需要把页移到新生代了,但等这波操作结束了,后面还有可能再也没有请求访问这些页面了,但因为这波扫描把热点数据都淘汰了,这就不太美丽了。

于是乎搞了个时间窗口,新页面在 1s 内的访问,并不会将其移到新生代,这样就不会淘汰热点数据了,然后 1s 后如果这个页面再次被访问,才会被移到新生代,这次访问大概率已经是别的业务请求,也说明这个数据确实可能是热点数据。

经过这两个改造, innodb 就解决了预读失效和一次性大批量数据访问的问题。

至此,对 buffer pool 的了解就差不多了。

change buffer

从上面的图我们可以看到, buffer pool 里面其实有一块内存是留给 change buffer 用的。

那 change buffer 是个啥玩意呢?

还记得我在 buffer pool 写的一句话吗:innodb 直接修改内存中的数据,然后到一定时机才会将这些脏数据刷到磁盘上。

也就是修改的时候直接修改的是 buffer pool 中的数据,但这个前提是 buffer pool 中已经存在你要修改的数据。

假设我们就直接执行一条 update table set name = 'yes' where id = 1,如果此时 buffer pool 里没有 id 为 1 的这条数据,那怎么办?

难道把这条数据先加载到 buffer pool 中,然后再执行修改吗?

当然不是,这时候 change buffer 就上场了。

如果当前数据页不在 buffer pool 中,那么 innodb 会把更新操作缓存到 change buffer 中,当下次访问到这条数据后,会把数据页加载到 buffer pool 中,并且应用上 change buffer 里面的变更,这样就保证了数据的一致性。

所以 change buffer 有什么好处?

二级索引页不在 buffer pool 中时,change buffer 可以避免立即从磁盘读取对应索引页导致的昂贵的随机I/O ,对应的更改可以在后面当二级索引页读入 buffer pool 时候被批量应用。

看到我加粗的字体没,二级索引页,没错 change buffer 只能用于二级索引的更改,不适用于主键索引,空间索引以及全文索引。

还有,唯一索引也不行,因为唯一索引需要读取数据然后检查数据的一致性。

看到这肯定又有小伙伴关心:更改先缓存在 change buffer 中,假如数据库挂了,更改不是丢了吗?

别怕,change buffer 也是要落盘存储的,从上图我们看到 change buffer 会落盘到系统表空间里面,然后 redo log 也会记录 chang buffer 的修改来保证数据一致性。

至此,想必你对 change buffer 已经有一定了解了吧。

这玩意主要用来避免于二级索引页修改产生的随机I/O,如果你的内存够大能装下所有数据,或者二级索引很少,或者你的磁盘是固态的对随机访问影响不大,其实可以关闭 change buffer,这玩意也增加了复杂度,当然最终还是得看压测结果。

Log Buffer

接下来,我们看看 Log Buffer。

从上面的图我们可以得知,这玩意就是给 redo log 做缓冲用的。

redo log 我们都知道是重做日志,用来保证崩溃恢复数据的正确性,innodb 写数据时是先写日志,再写磁盘数据,即 WAL (Write-Ahead Logging),把数据的随机写入转换成日志的顺序写。

但,即使是顺序写 log ,每次都调用 write 或者 fsync 也是有开销的,毕竟也是系统调用,涉及上下文切换。

于是乎,搞了个 Log Buffer 来缓存 redo log 的写入。

即写 redo log 先写到 Log Buffer 中,等一定时机再写到 redo log 文件里。

我们每次事务可能涉及多个更改,这样就会产生多条 redo log,这时会都先写到 Log Buffer 中,等事务提交的时候,一起将这些 redo log 写到文件里。

或者当 Log Buffer 超过总量的一半(默认总量是 16mb),也会将数据刷到 redo log 文件中。

也有个后台线程,每隔 1s 就会将 Log Buffer 刷到 redo log 文件中。

从上面这些我们得知,Log Buffer 其实就是一个写优化操作,把多次 write 优化成一次 write,一次处理多数据,减少系统调用。

看到这肯定有小伙伴说,数据先写 Log Buffer 而不刷盘,这数据不会丢吗?

innodb 其实给了个配置,即 innodb_flush_log_at_trx_commit 来控制 redo log 写盘时机。

  • 当值为 0,提交事务不会刷盘到 redo log,需要等每隔一秒的后台线程,将 log buffer 写到操作系统的 cache,并调用 fsync落盘,性能最好,但是可能会丢 1s 数据。
  • 当值为 1,提交事务会将 log buffer 写到操作系统的 cache,并调用 fsync 落盘,保证数据正确,性能最差,这也是默认配置。
  • 当值为 2,提交事务会将 log buffer 写到操作系统的 cache,但不调用 fsync,而是等每隔 1s 调用 fsync 落盘,性能折中,如果数据库挂了没事,如果服务器宕机了,会丢 1s 数据。

具体如何配置看你的业务了。

至此,想必你应该了解 Log Buffer 是干啥用了的吧。

Doublewrite Buffer

现在我们来看最后一个 buffer,即 Doublewrite Buffer。

这玩意又是啥用呢?

我们都知道 innodb 默认一页是 16K,而操作系统 linux 内存页是 4K,那么一个 innodb 页对应 4 个系统页。

所以 innodb 的一页数据要刷盘等于需要写四个系统页,那假设一页数据落盘的时候,只写了一个系统页 就断电了,那 innodb 一页数据就坏了,然后就 g 了,恢复不了。

即产生了部分页面写问题,因为写 innodb 的一页无法保证原子性,所以引入了 Doublewrite Buffer。

其实就是当 innodb 要将数据落盘的时候,先将页数据拷贝到 Doublewrite Buffer 中,然后 Doublewrite Buffer 再刷盘到 Doublewrite Buffer Files,这时候数据已经落盘了。

然后再将数据页刷盘到本该到文件上。

从这个步骤我们得知,数据是写了两次磁盘,所以这玩意叫 double write。

之所以这样操作就是先找个地方暂存这次刷盘的完整数据,如果出现断电这种情况导致的部分页面写而损坏原先的完整页,可以从 Doublewrite Buffer Files 恢复数据。

但虽然是两次写,性能的话也不会低太多,因此数据拷贝到 Doublewrite Buffer 是内存拷贝操作,然后写到 Doublewrite Buffer Files 也是批量写,且是顺序写盘,所以整体而已,性能损失不会太多。

有了这玩意,在崩溃恢复的时候,如果发现页损坏,就可以从 Doublewrite Buffer Files 里面找到页副本,然后恢复即可。

完美。

最后

好了,关于 innodb buffer 们介绍完了,我们来总结一下:

  • buffer pool,缓存数据页,避免频繁地磁盘交互,内部利用定制的 LRU 来淘汰缓存页,LRU分老年代和新生代。
  • change buffer,用于二级非唯一索引的新增、修改或删除操作,当 buffer pool 没有数据的时候,不需要加载数据页到缓冲池中,而是通过 change buffer 来记录此变更,减少随机 I/O。
  • log buffer,用于 redo log 的缓冲,减少 I/O,批处理减少系统调用。
  • doublewrite buffer,用于部分页面写问题,双写数据页,坏页时可从 doublewrite buffer files 进行页恢复,保证页的完整和数据的正确。

>>>>参考资料

  • https://dev.mysql.com/doc/refman/8.0/en/innodb-storage-engine.html

作者丨是YES呀

来源丨公众号:yes的练级攻略(ID:yes_JAVA



Tags:MySQL   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
MySQL 核心模块揭秘
server 层会创建一个 SAVEPOINT 对象,用于存放 savepoint 信息。binlog 会把 binlog offset 写入 server 层为它分配的一块 8 字节的内存里。 InnoDB 会维护自己的 savepoint...【详细内容】
2024-04-03  Search: MySQL  点击:(6)  评论:(0)  加入收藏
MySQL 核心模块揭秘,你看明白了吗?
为了提升分配 undo 段的效率,事务提交过程中,InnoDB 会缓存一些 undo 段。只要同时满足两个条件,insert undo 段或 update undo 段就能被缓存。1. 关于缓存 undo 段为了提升分...【详细内容】
2024-03-27  Search: MySQL  点击:(11)  评论:(0)  加入收藏
MySQL:BUG导致DDL语句无谓的索引重建
对于5.7.23之前的版本在评估类似DDL操作的时候需要谨慎,可能评估为瞬间操作,但是实际上线的时候跑了很久,这个就容易导致超过维护窗口,甚至更大的故障。一、问题模拟使用5.7.22...【详细内容】
2024-03-26  Search: MySQL  点击:(10)  评论:(0)  加入收藏
从 MySQL 到 ByteHouse,抖音精准推荐存储架构重构解读
ByteHouse是一款OLAP引擎,具备查询效率高的特点,在硬件需求上相对较低,且具有良好的水平扩展性,如果数据量进一步增长,可以通过增加服务器数量来提升处理能力。本文将从兴趣圈层...【详细内容】
2024-03-22  Search: MySQL  点击:(24)  评论:(0)  加入收藏
MySQL自增主键一定是连续的吗?
测试环境:MySQL版本:8.0数据库表:T (主键id,唯一索引c,普通字段d)如果你的业务设计依赖于自增主键的连续性,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不...【详细内容】
2024-03-10  Search: MySQL  点击:(6)  评论:(0)  加入收藏
准线上事故之MySQL优化器索引选错
1 背景最近组里来了许多新的小伙伴,大家在一起聊聊技术,有小兄弟提到了MySQL的优化器的内部策略,想起了之前在公司出现的一个线上问题,今天借着这个机会,在这里分享下过程和结论...【详细内容】
2024-03-07  Search: MySQL  点击:(28)  评论:(0)  加入收藏
MySQL数据恢复,你会吗?
今天分享一下binlog2sql,它是一款比较常用的数据恢复工具,可以通过它从MySQL binlog解析出你要的SQL,并根据不同选项,可以得到原始SQL、回滚SQL、去除主键的INSERT SQL等。主要...【详细内容】
2024-02-22  Search: MySQL  点击:(45)  评论:(0)  加入收藏
如何在MySQL中实现数据的版本管理和回滚操作?
实现数据的版本管理和回滚操作在MySQL中可以通过以下几种方式实现,包括使用事务、备份恢复、日志和版本控制工具等。下面将详细介绍这些方法。1.使用事务:MySQL支持事务操作,可...【详细内容】
2024-02-20  Search: MySQL  点击:(53)  评论:(0)  加入收藏
为什么高性能场景选用Postgres SQL 而不是 MySQL
一、 数据库简介 TLDR;1.1 MySQL MySQL声称自己是最流行的开源数据库,它属于最流行的RDBMS (Relational Database Management System,关系数据库管理系统)应用软件之一。LAMP...【详细内容】
2024-02-19  Search: MySQL  点击:(38)  评论:(0)  加入收藏
MySQL数据库如何生成分组排序的序号
经常进行数据分析的小伙伴经常会需要生成序号或进行数据分组排序并生成序号。在MySQL8.0中可以使用窗口函数来实现,可以参考历史文章有了这些函数,统计分析事半功倍进行了解。...【详细内容】
2024-01-30  Search: MySQL  点击:(54)  评论:(0)  加入收藏
▌简易百科推荐
MySQL 核心模块揭秘
server 层会创建一个 SAVEPOINT 对象,用于存放 savepoint 信息。binlog 会把 binlog offset 写入 server 层为它分配的一块 8 字节的内存里。 InnoDB 会维护自己的 savepoint...【详细内容】
2024-04-03  爱可生开源社区    Tags:MySQL   点击:(6)  评论:(0)  加入收藏
MySQL 核心模块揭秘,你看明白了吗?
为了提升分配 undo 段的效率,事务提交过程中,InnoDB 会缓存一些 undo 段。只要同时满足两个条件,insert undo 段或 update undo 段就能被缓存。1. 关于缓存 undo 段为了提升分...【详细内容】
2024-03-27  爱可生开源社区  微信公众号  Tags:MySQL   点击:(11)  评论:(0)  加入收藏
MySQL:BUG导致DDL语句无谓的索引重建
对于5.7.23之前的版本在评估类似DDL操作的时候需要谨慎,可能评估为瞬间操作,但是实际上线的时候跑了很久,这个就容易导致超过维护窗口,甚至更大的故障。一、问题模拟使用5.7.22...【详细内容】
2024-03-26  MySQL学习  微信公众号  Tags:MySQL   点击:(10)  评论:(0)  加入收藏
从 MySQL 到 ByteHouse,抖音精准推荐存储架构重构解读
ByteHouse是一款OLAP引擎,具备查询效率高的特点,在硬件需求上相对较低,且具有良好的水平扩展性,如果数据量进一步增长,可以通过增加服务器数量来提升处理能力。本文将从兴趣圈层...【详细内容】
2024-03-22  字节跳动技术团队    Tags:ByteHouse   点击:(24)  评论:(0)  加入收藏
MySQL自增主键一定是连续的吗?
测试环境:MySQL版本:8.0数据库表:T (主键id,唯一索引c,普通字段d)如果你的业务设计依赖于自增主键的连续性,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不...【详细内容】
2024-03-10    dbaplus社群  Tags:MySQL   点击:(6)  评论:(0)  加入收藏
准线上事故之MySQL优化器索引选错
1 背景最近组里来了许多新的小伙伴,大家在一起聊聊技术,有小兄弟提到了MySQL的优化器的内部策略,想起了之前在公司出现的一个线上问题,今天借着这个机会,在这里分享下过程和结论...【详细内容】
2024-03-07  转转技术  微信公众号  Tags:MySQL   点击:(28)  评论:(0)  加入收藏
MySQL数据恢复,你会吗?
今天分享一下binlog2sql,它是一款比较常用的数据恢复工具,可以通过它从MySQL binlog解析出你要的SQL,并根据不同选项,可以得到原始SQL、回滚SQL、去除主键的INSERT SQL等。主要...【详细内容】
2024-02-22  数据库干货铺  微信公众号  Tags:MySQL   点击:(45)  评论:(0)  加入收藏
如何在MySQL中实现数据的版本管理和回滚操作?
实现数据的版本管理和回滚操作在MySQL中可以通过以下几种方式实现,包括使用事务、备份恢复、日志和版本控制工具等。下面将详细介绍这些方法。1.使用事务:MySQL支持事务操作,可...【详细内容】
2024-02-20  编程技术汇    Tags:MySQL   点击:(53)  评论:(0)  加入收藏
MySQL数据库如何生成分组排序的序号
经常进行数据分析的小伙伴经常会需要生成序号或进行数据分组排序并生成序号。在MySQL8.0中可以使用窗口函数来实现,可以参考历史文章有了这些函数,统计分析事半功倍进行了解。...【详细内容】
2024-01-30  数据库干货铺  微信公众号  Tags:MySQL   点击:(54)  评论:(0)  加入收藏
mysql索引失效的场景
MySQL中索引失效是指数据库查询时无法有效利用索引,这可能导致查询性能显著下降。以下是一些常见的MySQL索引失效的场景:1.使用非前导列进行查询: 假设有一个复合索引 (A, B)。...【详细内容】
2024-01-15  小王爱编程  今日头条  Tags:mysql索引   点击:(85)  评论:(0)  加入收藏
站内最新
站内热门
站内头条