MySQL

洞悉MySQL底层架构与SQL调优本质
帅旋
关注
充电
IT宅站长,技术博主,架构师,全网id:arthinking。

InnoDB执行引擎内幕:Buffer Pool

发布于 2020-05-30 | 更新于 2024-05-16

image-20200530224418964

buffer pool缓冲池)是主内存中的一个区域,在InnoDB访问表数据索引数据的时候,会顺便把对应的数据页缓存到缓冲池中。如果直接从缓冲池中直接读取数据将会加快处理速度。在专用服务器上,通常将80%左右的物理内存分配给缓冲池。

为了提高缓存管理效率,缓冲池把页面链接为列表,使用改进版的LRU算法将很少使用的数据从缓存中老化淘汰掉。

1、缓冲池LRU算法

通过使用改进版的LRU算法来管理缓冲池列表。

当需要把新页面存储到缓冲池中的时候,将淘汰最近最少使用的页面,并将新页面添加到旧子列表的头部。

image-20200519225450188

其中:

  • 旧子列表:也可以成为Old区,存储冷数据
  • 新子列表:也成为Young区,存储热数据

该算法运行方式:

  • 默认 3/8缓冲池用于旧子列表;
  • 当新页面加入缓冲池时,首先将其插入旧子列表头部
  • 重复访问旧子列表的页面,将使其移动至新子列表的头部;
  • 随着数据库的运行,页面逐步移至列表尾部,缓冲池中未被访问的页面最终将被老化淘汰。

相关优化参数:

  • innodb_old_blocks_pct:控制LRU列表中旧子列表的百分比,默认是37,也就是3/8,可选范围为5~95;
  • innodb_old_blocks_time :指定第一次访问页面后的时间窗口,该时间窗口内访问页面不会使其移动到LRU列表的最前面。默认是1000,也就是1秒。

innodb_old_blocks_time很重要,有了这1秒,对于全表扫描,由于是顺序扫描的,一般同一个数据页的数据都是在一秒内访问完成的,不会升级到新子列表中,一直在旧子列表淘汰数据,所以不会影响到新子列表的缓存。

2、关于磁盘IO的方式

image-20200530224453194

O_DIRECTinnodb_flush_method参数的一个可选值。

这里先介绍下和数据库性能密切相关的文件IO操作方法

2.1、文件IO操作方法

数据库系统是基于文件系统的,其性能和设备读写的机制有密切的关系。

open:打开文件[1]

1
int open(const char *pathname, int flags);

系统调用Open会为该进程一个文件描述符fd,常用的flags如下:

  • O_WRONLY:表示我们以"写"的方式打开,告诉内核我们需要向文件中写入数据;
  • O_DSYNC:每次write都等待物理I/O完成,但是如果写操作不影响读取刚写入的数据,则不等待文件属性更新;
  • O_SYNC:每次write都等到物理I/O完成,包括write引起的文件属性的更新;
  • O_DIRECT:执行磁盘IO时绕过缓冲区高速缓存(内核缓冲区),从用户空间直接将数据传递到文件或磁盘设备,称为直接IO(direct IO)。因为没有了OS cache,所以会O_DIRECT降低文件的顺序读写的效率。

write:写文件[2]

1
ssize_t write(int fd, const void *buf, size_t count);

使用open打开文件获取到文件描述符之后,可以调用write函数来写文件,具体表现根据open函数参数的不同而不同弄。

fsync & fdatasync:刷新文件[3]

1
2
3
4
5
#include <unistd.h>

int fsync(int fd);

int fdatasync(int fd);
  • fdatasync:操作完write之后,我们可以调用fdatasync将文件数据块flush到磁盘,只要fdatasync返回成功,则可以认为数据已经写到磁盘了;
  • fsync:与O_SYNC参数类似,fsync还会更新文件metadata到磁盘;
  • sync:sync只是将修改过的块缓冲区写入队列,然后就返回,不等实际写磁盘操作完成;

为了保证文件更新成功持久化到硬盘,除了调用write方法,还需要调用fsync。

大致交互流程如下图:

image-20200520224027300

更多关于磁盘IO的相关内容,可以阅读:On Disk IO, Part 1: Flavors of IO[4]

**fsync性能问题:**除了刷脏页到磁盘,fsync还会同步文件metadata,而文件数据和metadata通常存放在磁盘不同地方,所以fsync至少需要两次IO操作。

对fsync性能的优化建议:由于以上性能问题,如果能够减少metadata的更新,那么就可以使用fdatasync了。因此需要确保文件的尺寸在write前后没有发生变化。为此,可以创建固定大小的文件进行写,写完则开启新的文件继续写。

2.2、innodb_flush_method

innodb_flush_method定义用于将数据刷新InnoDB数据文件日志文件的方法,这可能会影响I/O吞吐量。

以下是具体参数说明:

属性
命令行格式 –innodb-flush-method=value
系统变量 innodb_flush_method
范围 全局
默认值(Windows) unbuffered
默认值(Unix) fsync
有效值(Windows) unbuffered, normal
有效值(Unix) fsync, O_DSYNC, littlesync, nosync, O_DIRECT, O_DIRECT_NO_FSYNC

比较常用的是这三种:

fsync

默认值,使用fsync()系统调用来flush数据文件和日志文件到磁盘;

O_DSYNC

由于open函数的O_DSYNC参数在许多Unix系统上都存中问题,因此InnoDB不直接使用O_DSYNC。

InnoDB用于O_SYNC 打开和刷新日志文件,fsync()刷新数据文件。

表现为:写日志操作是在write函数完成,数据文件写入是通过fsync()系统调用来完成;

O_DIRECT

使用O_DIRECT (在Solaris上对应为directio())打开数据文件,并用于fsync()刷新数据文件和日志文件。此选项在某些GNU/Linux版本,FreeBSD和Solaris上可用。

表现为:数据文件写入直接从buffer pool到磁盘,不经过操作系统缓冲,日志还是需要经过操作系统缓存;

O_DIRECT_NO_FSYNC

在刷新I/O期间InnoDB使用O_DIRECT,并且每次write操作后跳过fsync()系统调用。

此设置适用于某些类型的文件系统,但不适用于其他类型的文件系统。例如,它不适用于XFS。如果不确定所使用的文件系统是否需要fsync()(例如保留所有文件元数据),请改用O_DIRECT。

如下图所示:

image-20200520232414096

为什么使用了O_DIRECT配置后还需要调用fsync()?

参考MySQL的这个bug:Innodb calls fsync for writes with innodb_flush_method=O_DIRECT[5]

Domas进行的一些测试表明,如果没有fsync,某些文件系统(XFS)不会同步元数据。如果元数据会更改,那么您仍然需要使用fsync(或O_SYNC来打开文件)。

例如,如果在启用O_DIRECT的情况下增大文件大小,它仍将写入文件的新部分,但是由于元数据不能反映文件的新大小,因此如果此刻系统发生崩溃,文件尾部可能会丢失。

为此:当重要的元数据发生更改时,请继续使用fsync或除O_DIRECT之外,也可以选择使用O_SYNC。

MySQL从v5.6.7起提供了O_DIRECT_NO_FSYNC选项来解决此类问题。

References


  1. Linux Programmer’s Manual - OPEN(2). (2020-02-09). Retrieved from http://man7.org/linux/man-pages/man2/open.2.html ↩︎

  2. man-pages.write. (2019-10-10). Retrieved from http://man7.org/linux/man-pages/man2/write.2.html ↩︎

  3. man-pages.fdatasync. (2019-03-06). Retrieved from http://man7.org/linux/man-pages/man2/fdatasync.2.html ↩︎

  4. On Disk IO, Part 1: Flavors of IO. medium.com. Retrieved from https://medium.com/databasss/on-disk-io-part-1-flavours-of-io-8e1ace1de017 ↩︎

  5. Innodb calls fsync for writes with innodb_flush_method=O_DIRECT. Retrieved from https://bugs.mysql.com/bug.php?id=45892 ↩︎

本文作者: 帅旋

本文链接: https://www.itzhai.com/columns/mysql/innodb/buffer-pool.html

版权声明: 版权归作者所有,未经许可不得转载,侵权必究!联系作者请加公众号。

×
IT宅

关注公众号及时获取网站内容更新。

请帅旋喝一杯咖啡

咖啡=电量,给帅旋充杯咖啡,他会满电写代码!

IT宅

关注公众号及时获取网站内容更新。