暂无图片
暂无图片
7
暂无图片
暂无图片
暂无图片

MySQL 底层数据&日志刷新策略解读

原创 CuiHulong 2025-02-11
173

对于数据库系统来说,保证数据不丢失是核心的机制,那就必须刷新到底层文件中,WAL机制或保存多副本。

在MySQL中innodb_flush_method参数影响着InnoDB存储引擎如何将数据和日志刷新到磁盘上。下面先了解下官方对于底层新数据和日志刷新策略。
image.png

从提供的参数配置里目前支持6种方式:

  • fsync:InnoDB引擎使用fsync()系统调用来刷新数据和日志文件。
  • O_DSYNC:InnoDB引擎使用O_SYNC打开和刷新日志文件,并使用fsync()刷新数据文件
  • O_DIRECT:InnoDB引擎使用O_DIRECT打开数据文件,并使用fsync()刷新数据和日志文件。
  • O_DIRECT_NO_FSYNC:InnoDB引擎在刷新I/O期间使用O_DIRECT,但在每次写入操作后跳过FSYNC()系统调用。
  • littlesync或2:此选项用于内部性能测试,目前不受支持。使用风险自负。
  • nosync或3:此选项用于内部性能测试,目前不受支持。使用风险自负。

刷新机制分成两部分内容。一是实际数据,二是日志文件(就是Redo日志)。可以归纳如下:

fsync O_DSYNC O_DIRECT
数据 直接刷新 刷新数据 跳过内存缓冲区
日志 直接刷新 跳过内存缓冲区 跳过内存缓冲区

下面了解下fsync,O_DSYNC,O_DIRECT具体如何实现I/O刷新。

IO缓冲层次关系

通常的文件写操作需要经过用户空间->内核空间->存储。如接口流程图所示,IO写入是采取stdio函数库和内核采用的缓冲。首先,通过stdio库将用户数据传递到stdio缓冲区,该缓冲区位于用户态内存区。当缓冲区填满,stdio库会调用write()系统调用,将数据传递到内核高速缓冲区,该缓冲区位于内核态内存区。之后,内核发起磁盘操作。
image.png

结合MySQL刷新数据策略,其中fsync不通过stdio缓冲区 和 内核缓冲区直接写入磁盘。
open(O_DIRECT和O_SYNC)是系统调用flag参数标志位,绕过内核缓冲区高速缓存‌,控制文件打开时的同步行为。比如,当使用O_SYNC标志打开一个文件时,系统会确保所有的写入操作都同步到磁盘上,即每次写入后都会等待数据真正写入到物理存储设备后才返回。

  • O_SYNC:要求任何写入操作都要阻塞,直到所有数据和元数据都已写入持久存储。
  • O_DSYNC:与O_SYNC类似,只是不需要等待任何元数据更改,这些更改对于读取刚刚写入的数据来说是不必要的。在实践中,O_DSYNC意味着应用程序不需要等到辅助信息(例如文件修改时间)写入磁盘。使用O_DSYNC而不是O_SYNC通常可以消除在写入时刷新文件索引节点的需要。
  • O_DIRECT:称为直接IO(direct IO)或者裸IO(raw IO)。也会传输数据,但采取的是无缓冲的方式,保证数据一定持久化磁盘。若一进程以O_DIRECT标志打开某文件,而另一进程以普通(即使用了高速缓存缓冲区)打开同一文件,则由直接IO所读写的数据与缓冲区高速缓存中内容之间不存在一致性。

其中,O_DIRECT与O_DSYNC的区别在于,O_DSYNC完整的走完了 “用户空间->内核空间->存储” 这一整个链条,并且在最后使用系统调用确保数据从文件系统缓存FLUSH到磁盘。而O_DIRECT因为绕过了内核空间,路径为“用户空间->存储”,直接落盘,并不需要FLUSH。但O_DIRECT由此而来的问题是,内核处理的所有事情都需要用户自己来处理,其中最重要的是内存对齐–这件小事通常是内核来处理的。而对齐的大小,则是根据存储的sector size扇区大小

性能方面

按照单一数据刷新,性能方面耗时fsync > O_SYNC > O_DSYNC > O_DIRECT。网上某大人测试过,
采用O_SYNC标志 或 频繁调用fsync() 比无O_SYNC时时间相差10~1000多倍。(仅作为参考)
image.png

也可以通过dd命令测试实际性能,其中在oflag里添加sync,O_DIRECT等。同时查看执行时间和buff/cache的变化等。

#清理缓存:
shell# echo 3 > /proc/sys/vm/drop_caches
shell# time dd if=/dev/zero of=/dev/bcache0 bs=1M count=1024 oflag=sync
dd: error writing '/dev/bcache0': No space left on device
899+0 records in
898+0 records out
941850624 bytes (942 MB, 898 MiB) copied, 0.735089 s, 1.3 GB/s

#dd without direct
shell# time dd if=/dev/zero of=/dev/bcache0 bs=1M count=1024 
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 27.8166 s, 38.6 MB/s

#dd with direct
shell# time dd if=/dev/zero of=/dev/bcache0 bs=1M count=1024 oflag=direct
。。。

选择策略

如何选择策略,官方建议如下:

  • 在类Unix系统上,默认值是fsync。在Windows上,默认值是无缓冲的。
  • 在具有硬件RAID控制器和电池备份写缓存的系统上,O_DIRECT可以帮助避免InnoDB缓冲池和操作系统文件系统缓存之间的双重缓冲。
  • 在某些InnoDB数据和日志文件位于SAN上的系统上,对于以SELECT语句为主的读取繁重的工作负载,默认值或O_DSYNC可能会更快。

在普遍场景中,MySQL数据库服务应该是响应快,又保证数据不丢失根本。所以I/O负载高的配置上采用,可以采取O_DIRECT(减少了中间环节,从而降低了延迟)方式。因为MySQL中对底层数据文件的操作,会进行锁控制,所以不会存在同时对某个数据文件写入的操作,其中O_DIRECT_NO_FSYNC方式,从MySQL 8.0.14开始,在创建新文件、增加文件大小和关闭文件后调用fsync(),以确保文件系统元数据更改同步。从而进一步提升系统性能。这个需要注意O_DIRECT要求内存地址与扇区大小对齐。

除此之外,如果数据文件和Redo日志文件驻留在不同的存储设备上,并且在从非电池备份的设备缓存中刷新数据文件写入之前发生意外退出,则可能会发生数据丢失。如果使用或打算使用不同的存储设备来存储重做日志文件和数据文件,并且数据文件驻留在具有非电池备份缓存的设备上,建议使用O_DIRECT。

总结

选择合适的innodb_flush_method可以提高I/O性能,特别是在数据库运行在高I/O负载下时。在MySQL中即使在系统崩溃或异常中断的情况下,也通过Redo和Doublewrite恢复数据的一致性,确保数据不会丢失。

最后修改时间:2025-02-11 17:51:17
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论