doublewrite 缓冲区是一个InnoDB在将页写到InnoDB数据文件中的适当位置之前的存储区域,这个存储区域中写入从缓冲池中刷新的页。如果在页写过程中出现了操作系统、存储子系统或意外的 mysqld 进程退出,InnoDB 可以在崩溃恢复期间从 doublewrite 缓冲区中找到一个好的页副本。
虽然数据被写入两次,但 doublewrite 缓冲区并不需要两倍的I/O开销或两倍的I/O操作。数据在一个大的顺序块中写入 doublewrite 缓冲区,通过对操作系统的单个 fsync() 调用(除非 innodb_flush_method
设置为 O_DIRECT_NO_FSYNC
)。
在MySQL 8.0.20之前,doublewrite 缓冲区存储区域位于InnoDB系统表空间中。从MySQL 8.0.20开始,doublewrite 缓冲区存储区域位于 doublewrite 文件中。
以下变量用于配置 doublewrite 缓冲区:
innodb_doublewrite:
变量 innodb_doublewrite
控制是否启用 doublewrite 缓冲区。在大多数情况下,默认启用它。要禁用 doublewrite 缓冲区,请将 innodb_doublewrite
设置为0或启动服务器,使用 ——skip-innodb-doublewrite
。如果您更关心性能而不是数据完整性,比如在执行基准测试时,可能会考虑禁用 doublewrite 缓冲区。
如果 doublewrite 缓冲区位于支持原子写的 Fusion-io 设备上,则会自动关闭 doublewrite 缓冲区,而使用 Fusion-io 原子写进行数据文件写。但是,注意 innodb_doublewrite 设置是全局的。禁用 doublewrite 缓冲区时,将禁用所有数据文件,包括不存在于 Fusion-io 硬件上的数据文件。该特性仅支持 Fusion-io 硬件,仅支持 Linux 下的 Fusion-io NVMFS。为了充分利用这个特性,建议使用 innodb_flush_method
为 O_DIRECT
设置。
innodb_doublewrite_dir:
innodb_doublewrite_dir
变量(在MySQL 8.0.20中引入)定义了 InnoDB 创建 doublewrite 文件的目录。如果不指定目录,则在 innodb_data_home_dir
目录下创建 doublewrite 文件。
散列符号“#”会自动添加到指定的目录名前,以避免与模式名冲突。然而,如果a '.'、'#' 或者在目录名中显式指定了'/'前缀,散列符号'#'不会作为目录名的前缀。
理想情况下,doublewrite目录应该放置在可用的最快存储介质上。
innodb_doublewrite_files:
变量 innodb_doublewrite_files
定义了 doublewrite 文件的数量。默认情况下,为每个缓冲池实例创建两个 doublewrite 文件:一个刷新列表 doublewrite 文件和一个 LRU 列表 doublewrite 文件。
刷新列表 doublewrite 文件用于从缓冲池刷新列表中刷新的页面。刷新列表 doublewrite 文件的默认大小是 InnoDB 页大小 * doublewrite 页
。
LRU 列表 doublewrite 文件用于从缓冲池LRU列表中刷新的页面。它还包含用于单页刷新的槽。LRU列表 doublewrite 文件的默认大小是 InnoDB页大小* (doublewrite 页数 +(512 缓冲池实例的数量))
,其中 512 是为单页刷新保留的槽总数。
至少有两个 doublewrite 文件。doublewrite 文件的最大数量是缓冲池实例数量的两倍。(缓冲池实例的数量由 innodb_buffer_pool_instances
变量控制。)
Doublewrite文件名的格式为: #ib_page_size_file_number.dblwr
。例如,下面的 doublewrite 文件是为 MySQL 实例创建的,InnoDB页面大小为16KB,只有一个缓冲池:
#ib_16384_0.dblwr
#ib_16384_1.dblwr
innodb_doublewrite_files
变量用于高级性能调优。默认设置应该适合大多数用户。
innodb_doublewrite_pages:
innodb_doublewrite_pages
变量(在MySQL 8.0.20中引入)控制每个线程的 doublewrite 页的最大数量。如果不指定,innodb_doublewrite_pages
设置为 innodb_write_io_threads
(InnoDB 中写操作的I/O线程数。缺省值为4) 的值。此变量用于高级性能调优。默认值应该适合大多数用户。
innodb_doublewrite_batch_size:
innodb_doublewrite_batch_size
变量(在MySQL 8.0.20中引入)控制批量写入doublewrite页面的数量。此变量用于高级性能调优。默认值应该适合大多数用户。
从MySQL 8.0.23开始,InnoDB 会自动加密属于加密表空间的 doublewrite 文件页。同样,属于页压缩表空间的 doublewrite 文件页也被压缩。因此,doublewrite 文件可以包含不同的页面类型,包括未加密和未压缩的页面、加密的页面、压缩的页面以及既加密又压缩的页面。
写在最后
关于 Doublewrite Buffer 主要解决了什么问题呢?这里做一个举例描述,假设一个数据页的大小是 16K,当把内存中的脏页写到数据文件时,写了4K 突然掉电,也就是只有 4k 的数据是最新的,那么磁盘数据文件这个数据页就是不完整的,是一个坏掉的数据页。redo只能加上旧的日志、校检完整的数据页恢复一个脏块,不能修复坏掉的数据页,所以这个数据就丢失了,可能造成数据不一致,所以需要double write。其实这是一个“部分写”问题,数据库写的最小单位为 page,默认情况下为 16 kB,操作系统写的最小单位可能为 4 KB,而磁盘又有着自己的最小写单位,512 字节,部分写的问题便是这样产生的。
那么 doublewrite 是如何解决这个问题呢?在将页从缓冲池写入数据文件前,先写入 doublewrite 文件,而后再写入表空间的数据文件。如果后续写入发生崩溃,那么可以从 doublewrite 发现一个副本,并将其写入到表空间,再应用到 redo Log,这就完成了恢复过程。
以上是查找资料后的理解,倘若有疑,还望君自解。




