史上最强解读:Oracle里面为什么没有double write?

导读:MySQL有double write机制,PostgreSQL有full page write机制,那么Oracle里面为什么没有类似机制呢?

近期看到朋友圈转发了几篇关于MySQL innodb double write的文章,感觉都还不错。突然想到为什么Oracle没有这个东西?PostgreSQL是否也有类似机制?

在网上搜了一下,发现有人之前简单写过类似文章,但是没有一篇能够完全分析透彻的的。

所以,我想来好好说一下这个问题。

首先MySQL 的double write的机制,是为了解决partial write的问题;那么为什么会存在partial write呢?这本质上是因为操作系统的最小IO单位一般为4kb;以前大家使用的sas、sata的sector 都是512。现在如果是nvme ssd之类,已经有4kb甚至16kb的sector了。之所以sector越来越大就是为了解决ssd容量的问题。

这里不扯太远了。简单地讲,由于数据库的最小IO单位是page或者block,MySQL 默认是16kb,而Oracle 大家通常默认使用8kb的block size。这就导致数据库的一次最小IO对于操作系统来讲,需要调用多次完成。这里以MySQL为例,每一次写16kb,假定os 最小IO为4kb;那么需要调用4次。那么问题就来了。

假设操作系统只执行了2次IO操作就crash了,这个page 不就写坏了吗?这就是partial write 。

为什么说MySQL 的double write 可以解决partial write的问题呢?在回答这个问题之前,我们先来看一下MySQL整个数据读取和写入的基本流程(这只是大致流程,实际要要复杂的多):

1、data page1从idb数据文件中被读取到buffer pool中(这里也涉及到各种LRU);
2、user A发起事务T1 开始修改 page1,此时page1 会变成一个脏页(dirty page);
3、同时MySQL会开始写redo log,当然这里是先将page1的变更写入redo buffer,并不会立刻落盘;
4、User A 开始发起commit提交这个事务T1;
5、此时MySQL会将redo buffer内容flush到redo log中,即完成此时的redo落盘操作;
6、完成日志落盘后,Page1 会被先通过memcopy到double write buffer中;注意其实double write buffer并不是内存结构,其实是在共享表空间ibdata中分配的一块连续区域,相当于是把磁盘当内存使用,大小默认是2M,分为2个部分,每个部分1M;根据16kb单位进行计算,大概就是128个page;
7、最后double write buffer被写满或者用户本身发起了刷脏行为,那么会把数据从double write buffer写入到数据文件中。

上述是大致流程;那么为什么说能解决partial write问题呢?

1)假设page1 写入到double write buffer 就失败了,那么这个page1 根本就不会被写入到数据文件落盘;如果此时数据库crash了,按照正常的恢复流程找检查的,应用redo,undo进行恢复即可。这没什么可说的。

2)假设写入到double write buffer成功,但是page1 在写入到数据文件中时失败,如何进行恢复呢?这就是我们要说的关键地方。由于double write buffer中是写成功了,数据文件写失败,那么在恢复时MySQL在检查这个page1时发现checksum有问题,会直接从double write buffer中copy 已经写完成的page1覆盖到数据文件对应位置,然后再去应用redo等等。

所以简单来讲,double write机制更像在数据落盘之前多了一层缓冲。那么这个机制是否有问题呢 ?我认为是存在一定问题的。

比如为了确保机制的实现,double write buffer并不是真正的内存不buffer,是从共享表空间中分配一块连续区域出来。这本质上是用磁盘当内存使用,虽然读写大都是顺序读写(实际上可能也存在随机single page flush的情况)。磁盘的操作毕竟比纯内存中的操作要慢一些,当然这个机制本身来讲并不会带来多大的性能衰减,因为就是普通的SAS 硬盘,顺序读写性能都是非常不错的。

另外为什么会出现这个问题呢?其实是因为很多存储设备不支持原子写,MySQL 目前暂时也不支持。不过MariaDB目前是支持这个特性的。(https://mariadb.com/kb/en/atomic-write-support/)

当然这是过去的时代,现在都是各种Fusion IO/PCIE SSD,大多都已经支持了原子写了。既然硬件已经支持,是否我们就可以在SSD方面关闭这个功能呢?很明显是可以的,既然都支持原子写了,还需要double write这种鸡肋的功能干什么呢?这本身就是一种不算太完美的方案。其实MySQL官方文章也直接说明了这个问题:

If system tablespace files (“ibdata files”) are located on Fusion-io devices that support atomic writes, doublewrite buffering is automatically disabled and Fusion-io atomic writes are used for all data files. Because the doublewrite buffer setting is global, doublewrite buffering is also disabled for data files residing on non-Fusion-io hardware. This feature is only supported on Fusion-io hardware and is only enabled for Fusion-io NVMFS on Linux. To take full advantage of this feature, an  innodb_flush_method setting of  O_DIRECT is recommended.

如果直接看源码你会看到如下的类似内容:

#if !defined(NO_FALLOCATE) && defined(UNIV_LINUX)
/* Note: This should really be per node and not per
tablespace because a tablespace can contain multiple
files (nodes). The implication is that all files of
the tablespace should be on the same medium. */


if (fil_fusionio_enable_atomic_write(it->m_handle)) {
  if (srv_use_doublewrite_buf) {
    ib::info(ER_IB_MSG_456) << "FusionIO atomic IO enabled,"
                              " disabling the double write buffer";


    srv_use_doublewrite_buf = false;
  }


  it->m_atomic_write = true;
} else {
  it->m_atomic_write = false;
}


#else
it->m_atomic_write = false;
#endif /* !NO_FALLOCATE && UNIV_LINUX*/

简单地讲,就是如果都使用了支持原子写的fusion-io 等存储设备,那么double write机制会被自动disable。同时官方建议将innodb_fush_method设置为o_direct,这样可以充分发挥硬件性能。

除了硬件等支持,那么传统等文件系统比如ext4/xfs/zfs/VxFS 是否支持原子写呢?

实际上ext4/xfs对原子写支持目前都不是非常优化,zfs是天然支持的,很早的版本就支持了,另类的文件系统;另外veritas的VxFS也是支持的,其官网有相关的解释。(https://sort.veritas.com/public/documents/vis/7.0/linux/productguides/html/infoscale_solutions/ch05s07.htm)

对存储这方面了解不多,不敢写太多,免得被碰。我们还是回到数据库这个层面上来。

国内也有一些硬件厂家在不断努力优化这个问题;例如Memblaze的混合介质的多命名空间管理解决方案(这里我不是打广告,只是网上搜到一点材料  https://cloud.tencent.com/developer/news/404974 )。从这个测试来看,percona mysql 8.0…15在启用parallel double write的情况下,性能有超过30%的提升,这还是非常不错的。注意这里的parallel 是根据buffer pool instance来的,提高并发处理能力。

实际上MySQL 原生版本8.0.20在double write方面做了重大改变(看上去有点抄袭xxx的做法),比如也可以把double write files进行单独存放了。

Prior to MySQL 8.0.20, the doublewrite buffer storage area is located in the  InnoDB system tablespace. As of MySQL 8.0.20, the doublewrite buffer storage area is located in doublewrite files.

也引入了类似Parallel的机制。如果查看官方文档能看到引入了类似如下的相关参数:

innodb_doublewrite_files(这个跟buffer pool instacne有关)
innodb_doublewrite_pages
innodb_doublewrite_batch_size

不难看出MySQL 8.0.20 在这方面做的很不错了,想必性能必定又上升一个台阶。对于这个我一直感觉比较奇怪:为什么会出现?

那么这个有没有可能跟MySQL 自身page的结构有关系呢?开始我以为可能是这个问题,后来发现其实并不是。从MySQL的源码来看,本身MySQL 也是需要检查page checksum的,否则怎么判断坏页呢?

/** Issue a warning when the checksum that is stored in the page is valid,
but different than the global setting innodb_checksum_algorithm.
@param[in]  curr_algo   current checksum algorithm
@param[in]  page_checksum   page valid checksum
@param[in]  page_id     page identifier */
void page_warn_strict_checksum(srv_checksum_algorithm_t curr_algo,
srv_checksum_algorithm_t page_checksum,
const page_id_t &page_id);

说完了MySQL,我们再来看看PostgreSQL(开源数据库永远的老二)是否也存在这个问题。由于我本身对PostgreSQL是不太熟的,所以查阅了相关文档后发现,PG在很早的版本就引入了full page write机制,来避免这种partial write问题。

这里我盗用一张图片来简单解释一下PostgreSQL的数据写入流程:

1、假设T1 时间点触发了checkpoint;
2、T2时间点开始将DML操作产生的变化刷到xlog(关系型数据库都是日志写优先原则);
3、T3时间点事务发起了commit提交操作;这个时候会将脏页变化落盘xlog(这时候仍然还没开始写脏页);
4、T3之后准备将脏数据flush 到datafile时,如果数据库这时候crash可能会是什么现象?

那么P

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值