0%

WiredTiger 日志组提交

Wiredtiger 中的日志系统设计参考了论文 Scalability of write-ahead logging on multicore and multisocket hardware,链接为 https://infoscience.epfl.ch/record/170505/files/aether-smpfulltext.pdf。

以下内容参考了官方文章 https://www.mongodb.com/blog/post/breaking-wired-tiger-logjam-write-ahead-log

日志组提交模式

用户在写日志时,对于日志缓冲区的竞争是无锁的,只使用原子标志来进行同步。日志写入的流程如下:

mlog

在 Slot 的竞争阶段,不同的线程会竞争修改Slot 的原子标志。若修改成功,则线程需要负责分配下一个 Slot;若修改失败,则会根据返回的 entry 位置,将日志拷贝到 Slot 中;如果下一个Slot 分配完成,则本 Slot 禁止写入,返回的 entry < 0,线程需要再次竞争下一个 Slot。

当线程完成 Slot 的写入,可以根据日志写入等级选择返回时机,而最后一个完成写入的线程则需要负责将 Slot 写入硬盘缓冲区,若该 Slot 中有现成要求写入硬盘,则进行刷盘操作。

自由调控日志等级

由于每一个用户线程都深度参与了日志的提交过程,用户线程具有三个时机可以返回:1、写入日志缓冲区。2、写入硬盘缓冲区。3、写入硬盘。这种自由度允许了 MongoDB 的 CRUD 操作具备了更高的调控等级:不写日志、日志写入缓冲区、日志写入硬盘,对于一些高写入压力的场景,如分布式日志,数据采集系统,可以根据具体情况来选择日志等级,加快客户端的提交速度。

轻量级缓冲区

相比于 InnoDB 中采用 Log File 缓冲区,WiredTiger 中的缓冲区量级要更小。这是因为 MongoDB 中需要写入日志的线程数量更多,经常会大于 CPU 的核数,如果采用大缓冲区,各个线程之间对锁的竞争力度会很大。采用更小的缓冲区,并且采用 join 机制,虽然各个线程仍然存在竞争,但是竞争失败并不会阻塞线程,只是会调整线程的活动模式。这种设计在面对短时间内的高并发写入,能够保证日志写入的高效性。

无后台线程负责写入日志

传统的后台线程写入的日志模式在无日志缓冲区时会陷入阻塞,而唤醒线程则需要花费 20us 左右的时间,而这一延迟按照论文中的方法是可以优化的,也就是 Wiredtiger 中采取的日志模式。当发生 少量写入操作到达———短时间空闲———少量写入操作到达,这种情况;传统的后台日志写入模式可能就会损耗较大的性能。

总结

Wiredtiger 中的日志组提交的设计来源于 MongoDB 的特殊需求:大量的事务线程同时提交事务,大量日志不需要较高的写入等级。该日志系统主要思想可以概括为以下几点:

  • 日志组提交采取锁来同步,但获取锁失败并不会阻塞线程
  • 并发写入日志缓冲区不需要加锁
  • 日志缓冲区写入硬盘需要使用版本号约束
  • 每个线程可以选择日志的同步等级
  • 不需要后台线程写入日志

最近,同课题组的师弟在学习 C++ 时遇到了一个奇怪的问题,使用openwrite系统调用后,程序的输出是正常的,但是无法正常显示文件的内容。出现问题的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main() {

int fd = ::open("111.txt",O_CREAT | O_WRONLY);

if(fd < 0){
std::cerr << errno;
return 1;
}

std::string str(1<< 10,'1');

size_t wr = ::write(fd,str.c_str(),str.size());

std::cout << "total write bytes: " <<wr << std::endl;

close(fd);
return 0;
}

// 程序输出为
total write bytes: 1024

但是,在 CLion 中打开目标文件,却不能显示任何内容:

image-20230126195448049

最初学弟以为是文件内容没有成功写入,在网上寻找答案未果。经过排查后,发现问题出现在文件的权限上,而非程序写入失败。在终端使用 ls 和 cat 命令可以验证这一猜想:

1
2
3
4
5
6
7
8
9
10
# 使用 ls 发现文件大小是 1024,证明写入成功,但权限非常奇怪
ls -l 111.txt
--w-r-x--- 1 tangrenchu staff 1024 Jan 26 19:58 111.txt

# 使用 cat 命令
cat 111.txt
zsh: permission denied: ./111.txt

sudo cat 111.txt
111111111111111111111111111...

这段代码的编译和运行环境为 MacOS,可能由于 MacOS 的权限分组比较奇怪,导致代码所在的用户组与登录用户不同,导致登录用户没有权限读取文件内容。