基于fsimage和checkpoint机制优化Master重启性能

52 阅读2分钟

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

基于 fsimage 和 checkpoint 机制优化 Master 重启性能

我们都知道单纯的 editslog 机制是可能存在 Master 重启时间很久的问题的,那这个问题怎么解决呢?

今天我们就一起来讨论下~

fsimage 机制

本来的设计,系统中的元数据是由 editslog 日志来持久化的,这时候,引进了 Fsimage 快照。

editslog 是命令日志, FsImage 是快照。

说到这里就不得不提一提复制状态机理论了。

其实我们接触到的很多分布式系统其实都是由复制状态机的身影的,复制状态机理论是基于快照 + 操作日志的。

我们熟悉的很多分布式系统都这样的:

Redis 集群,快照(RDB)+ 操作日志(AOF)

MySQL 集群,binlog + redo log

分布式存储, FsImage(快照) + editslog(操作日志)

FsImage 和 editslog 存储数据的区别:

FsImage:


public class FsImage {
    ......

    /**
     * 当前最大的txId
     */
    private long maxTxId;

    /**
     * 内容
     */
    private INode iNode;
    
    ......
}

可以看到 FsImage 中保存的是 iNode 的信息,也就是直接就是元数据信息;在 NameNode 重启的时候,只需要直接加载进内存即可。

editslog:

message EditLog {
  int64 txId = 1;
  // 操作类型
  int32 opType = 2;
  string path = 3;
  map<string, string> attr = 4;
}

可以看到 editslog 中保存的数据是包含具体操作的(opType),也就是说在回放 editslog 中的数据的时候,其实就是将当初的命令重新执行一次。

checkpoint 机制

Checkpoint机制是一种实现数据恢复的优化策略。它主要是指将fsimage快照文件定期保存到磁盘上,并将新的变更操作以editslog日志形式保存,这样NameNode守护进程在重启之后只需要加载fsimage快照文件即可,而不需要对所有editslog日志进行恢复,从而提高数据恢复的速度。

Checkpoint机制的实现流程:

  1. 保存fsimage快照文件
  2. 将新的变更操作记录在editslog日志中
  3. 在Master重启之后,NameNode守护进程只需要加载fsimage快照文件即可。

checkpoint 任务实现:

/**
 * checkpoint 任务
 */
@Override
public void run() {
    log.info("BackupNode启动checkpoint后台线程.");
    try {
        // 如果是正在恢复元数据,则直接返回
        if (nameSystem.isRecovering()) {
            log.info("正在恢复元数据...");
            return;
        }

        // 当前 maxid 和 之前记录的 maxid 相等
        if (nameSystem.getMaxTxId() == lastCheckpointTxId) {
            log.info("EditLog和上次没有变化,不进行checkpoint: [txId={}]", lastCheckpointTxId);
            return;
        }
        // 以下讨论的情况是:当前 maxid 和 之前记录的 maxid 不相等(大于)
        FsImage fsImage = nameSystem.getFsImage();
        // 更新记录中的 maxid
        lastCheckpointTxId = fsImage.getMaxTxId();
        // 路径
        String fsImageFile = backupNodeConfig.getFsImageFile(String.valueOf(System.currentTimeMillis()));

        log.info("开始执行checkpoint操作: [maxTxId={}]", fsImage.getMaxTxId());

        // 写入FsImage文件
        doCheckpoint(fsImage, fsImageFile);

        // 上传 FsImage 给 NameNode
        uploadFsImage(fsImageFile);

        // 删除旧的FSImage
        namenodeClient.getDefaultScheduler().scheduleOnce("删除FSImage任务", fsImageClearTask, 0);
    } catch (Exception e) {
        log.error("FSImageCheckPointer error:", e);
    }
}

通过上述代码,我们可以发现其实所谓的 checkpoint 机制就是定时生成新的 FsImage,并覆盖旧的 FsImage。