TXYH
那么,wal本身带来的好处除了可以异步提交真正的事务内容外(避免马上flush事务操作设计的整页数据)
基本上就是需要有多个操作并发,类似mysql的组提交,才能在一定程度上起到作用?
你说的大体上没有错,其实本质上就是,有WAL落盘提供了数据可靠性保障,实际的数据结构你可以慢点刷,(有可能并发情况下)短时间内来了多个修改,能乘机一次一起刷下去(当然WAL还是得乖乖一个一个刷)。
不过你小看了这么干对性能提升的有效性hhh。
需要意识到的,有这两个点:
- 刷一次 WAL 本身比刷一次实际数据页便宜得多。举个例子:一次更新可能涉及很多很多个页(比如一个大UPDATE,更新了表里一半多的记录的数据。另一个例子是 B+Tree 出现 node splitting,影响到很多个非叶子页),但是记录在WAL里可能就是一条短短的 UPDATE 语句的事情。
- 一旦有了WAL,你的实际数据脏页想多不频繁刷盘都可以。反正WAL已经写入磁盘中了,实际数据页你想一小时刷一次盘都可以,崩溃了从WAL恢复就行了。
所以,对于写比较频繁的场景来说,起到作用的程度那可大了。
其实本质上WAL顺序写能提高性能
这句话就说得模糊且带有歧义,而你是错误理解了这句话。
并不是WAL本身刷盘的时候的顺序写能提高性能,而是「因为有了WAL,所以数据页的修改可以(安全地)不马上刷盘,因 此对数据页的多次修改可合并为一次刷盘,从而提高性能」。和 WAL 顺不顺序写入没有关系。
WAL 的写入之间(没有group commit的前提下)也确实是认为总是随机的,但是一次随机 WAL 刷盘就是一次寻道+一次旋转延迟而已,相比我可能因此节省的几十上百个数据页的刷盘的耗时来说,简直就是弟弟(更别说大多数数据页的写入本身也是随机写入)。
题外话:这里插一句: WAL 的 group commit 不是免费的,在得到性能的同时,交换的要么是任务延迟,要么是可靠性。
那“顺序”两字到底从哪里来呢?
实际上如果单论 MySQL + InnoDB 的话,因为是 B+Tree,确实没什么好“顺序”的,先写WAL节省的其实是「某些页在短时间内重复的刷盘」造成的浪费。B+树本身就决定了读写多个节点之间不可能能有多顺序。
但是一些其他数据库设计,只要能够延迟刷盘,确实可以把原本相对随机的写入变成顺序的写入,最出名的例子就是 LSM Tree 数据库。
LSM Tree 数据库的设计是将数据分为多个 level,从 c0 到 ck 依次由热数据到冷数据分级,一般 c0 存在内存中,从 c1 开始落盘。
数据插入的时候,先把数据插入到 c0 的页中(LSM Tree 中的页称为 SSTable,页内数据有序), 由于 c0 是在内存中的,随机修改 c0 的性能特别的高(c0 的 SSTable 页一般用红黑树实现)。
然后,当 c0 中的页大/多到一定程度的时候,会发生 Compaction,即多个 c0 中的页可以合并成为一个更大的 c1 中的页,并刷到磁盘中,这个就是数据落盘的时间点。由于任何页内的数据都是有序的,多个 c0 中的页合并成一个 c1 中的页的过程,只需要一次简单的顺序归并即可,显然这个过程是顺序写盘的:
# pageA 和 pageB 在 c0
while pageA.hasLeft() and pageB.hasLeft():
if pageA.peek() < pageA.peek():
c1_newpage.append(pageA.next());
else:
c1_newpage.append(pageB.next());
c1_newpage.append(pageA.everythingLeft());
c1_newpage.append(pageB.everythingLeft());
题外话:这个归并过程可以一层一层类推直到 c2、c3、c4......原理相同,每一层页数量越来越少但是单个页大小越来越大。分多层的原因是为了适应不同的存储介质,比如可以 c0=内存 c1=本地固态硬盘 c2=本地机械硬盘 c3=远程冷数据
等等。
即:因为处于内存中所以随机写性能优良的 c0,扛下了相对随机的写请求,并通过 Compaction 落盘的方式,将 c0 中的数据顺序地写入 c1(处于硬盘上,随机写性能较差)。这里就是 LSM-Tree “利用不同存储介质的不同特性”的体现。
但是,c0 是存在内存中的,如果只有 Compaction 的时候才落盘,如何保证数据持久呢?当然还是依靠 WAL。数据写入 c0 之前,会先打 WAL。如果出现崩溃,则重启时需要从 WAL 恢复整个 c0。
当然我们前面已经提到了,写一次 WAL 的开销相比 c0 能避免的潜在随机写/写放大而言,几乎微不足道。并且只要没有 Compaction 导致磁头移走,磁盘平时也就只是需要写WAL,多次 WAL 写之间大概率不用寻道(可以理解为大多数时候 WAL 的写入也是顺序的,虽然并不是时间上严格连续)。这也是为什么说 LSM Tree “只做顺序写入”。
而假设不设置 c0,直接把数据写到磁盘 c1 上,因为每次写位置都比较随机,肯定是寻道寻到吐。所以额外写一个 WAL 来设置一层 c0 是远远值得的。
这里就是我猜为啥会说出WAL顺序写能提高性能
这句话了:因为假设没有 WAL 的话,就不能把 c0 放到内存了(因为不安全,可能丢数据),那这个用内存挡下随机写的设计就不能成功。WAL是必要条件之一,但不是充分条件,更不是这个设计的核心要义。所以这句话虽然理论上没有说错但是关注的重心不对,有很强的误导性。
WAL 不是这类数据库能实现顺序写盘的根本原因,LSM Tree 才是。
LSM Tree 简介:
LSM Tree 与 B+Tree 对比(读写放大与空间放大):