editslog 的刷盘机制

196 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情

editslog 的刷盘机制

我们已经知道了 editslog 是分布式存储系统中的一种操作日志,它是负责将 NameNode 中的元数据持久化的;那么今天我们就一起来学习一下 editslog 的刷盘机制。

是直接刷盘的吗?

editslog 是直接刷盘的吗?一生成 editslog 就直接刷盘??

其实不然!

我们都知道磁盘IO是很慢的,如果 editslog 是直接刷盘的话,那性能也太差了。

editslog 双缓冲机制

为了避免生成 editslog 后直接刷盘,所以提出了 editslog 双缓冲机制。

双缓冲的机制:设计两块缓存,不停的往其中一块缓冲区里去写,一旦写满了之后,就由一个线程把这个缓冲区的数据刷入磁盘;在刷磁盘之前,会做一个缓冲区的交换,把两块缓冲区交换一下,让后续的editslog写入另外一块空置的缓冲区里去,之前写满的一块缓冲区就可以刷入磁盘中了。

双缓冲的结构定义:

editslog 生成的数据都往 currentBuffer 写,每次都拿 syncBuffer 这个刷盘;其实它们的作用有点像是指针,后面的交互缓冲区,其实就是交换这两个指针的指向。

@Slf4j
public class DoubleBuffer {
    // 以下定义了两块缓冲区
    
    // 当前写缓冲区,editslog 生成的数据都往这写
    private EditLogBuffer currentBuffer;
    
    // 刷盘缓冲区,每次都拿这个刷盘
    private EditLogBuffer syncBuffer;
    ......
}

写入一条editlog:

向 currentBuffer 缓冲区中写入

/**
 * 写入一条editlog
 */
public void write(EditLogWrapper editLog) throws IOException {
    currentBuffer.write(editLog);
}

交换两块缓冲区:

交换 currentBuffer 和 syncBuffer 的指向

/**
 * 交换两块缓冲区
 */
public void setReadyToSync() {
    EditLogBuffer temp = currentBuffer;
    currentBuffer = syncBuffer;
    syncBuffer = temp;
}

把缓冲区的 editlog 数据刷新到磁盘:

EditslogInfo 是刷盘前 editslog 对应的磁盘文件的信息

/**
 * 把缓冲区的 editlog 数据刷新到磁盘
 */
public EditslogInfo flush() throws IOException {
    // 刷盘
    EditslogInfo editslogInfo = syncBuffer.flush();
    // 如果 editslogInfo 不为空,说明已经有数据成功刷盘了
    // 这时候可以清楚缓冲区中的数据了
    if (editslogInfo != null) {
        syncBuffer.clear();
    }
    // 返回 editslog 文件信息
    return editslogInfo;
}

刷盘的时机

这里的刷盘,我们又分为两种:一种是异步刷盘,另一种是强制刷盘。

异步刷盘的刷盘时机: 当缓冲区中的数据大小大于配置值的时候,触发异步刷盘。(每次往缓冲区中写入 editslog 的时候都会检查一下是否需要异步刷盘)

// 缓冲区中数据大小达到指定值的时候,才放行,执行异步刷盘
if (!editLogBuffer.shouldForceSync()) {
    return;
}

强制刷盘的刷盘时机: 关机的时候需要强制刷盘!

/**
 * 优雅停机
 * 强制把内存里的edits log刷入磁盘中
 */
public void shutdown() {
    log.info("Shutdown DiskNameSystem.");
    // 强制刷盘
    this.editLog.flush();
}