分布式系统模式4-Low-Water Mark

作者: Unmesh Joshi

译者: java达人

来源: https://martinfowler.com/articles/patterns-of-distributed-systems/

预写日志中的索引,表示可以丢弃日志的哪一部分。

问题

预写日志维护对持久性存储的每次更新。随着时间的流逝,它会无限增长。Segmented Log允许一次处理较小的文件,但是如果不检查,总磁盘存储量将无限制增长。

解决方案

有一种机制可以告诉日志记录机器可以安全地丢弃日志的哪一部分。该机制提供了最低的偏移量或low water mark,在此之前的日志可以丢弃。让任务在后台的单独线程中运行,该任务连续检查可以丢弃日志的哪一部分并删除磁盘上的文件。

this.logCleaner = newLogCleaner(config);
this.logCleaner.startup();

日志清理器可以实现为定时任务

public void startup() {
    scheduleLogCleaning();
}


private void scheduleLogCleaning() {
    singleThreadedExecutor.schedule(() -> {
        cleanLogs();
    }, config.getCleanTaskIntervalMs(), TimeUnit.MILLISECONDS);
}

基于快照的Low-Water Mark

大多数共识实现(例如Zookeeper或etcd(在RAFT中定义))都实现了快照机制。在此实现,存储引擎需要定期快照。除快照外,它还存储成功应用的日志索引。参考“Write-Ahead Log”模式中的简单键值存储实现,可以采取以下快照:

public SnapShot takeSnapshot() {
    Long snapShotTakenAtLogIndex = wal.getLastLogEntryId();
    return new SnapShot(serializeState(kv), snapShotTakenAtLogIndex);
}

一旦快照成功保存在磁盘上,日志管理器将获得Low-Water Mark,以丢弃较旧的日志。

List<WALSegment> getSegmentsBefore(Long snapshotIndex) {
    List<WALSegment> markedForDeletion = new ArrayList<>();
    List<WALSegment> sortedSavedSegments = wal.sortedSavedSegments;
    for (WALSegment sortedSavedSegment : sortedSavedSegments) {
        if (sortedSavedSegment.getLastLogEntryId() < snapshotIndex) {
            markedForDeletion.add(sortedSavedSegment);
        }
    }
    return markedForDeletion;
}

基于时间的Low-Water Mark

在某些系统中,不一定要使用日志来更新系统状态,可以在给定的时间窗口后丢弃日志,而不必等待任何其他子系统共享可以删除的最低日志索引。例如,在像Kafka这样的系统中,日志将保留7周;消息时间超过7周的所有日志段都将被丢弃。对于此实现,每个日志条目还包括创建时的时间戳。然后,日志清理器可以检查每个日志段的最后一个条目,并丢弃早于配置的时间窗口的日志段。

private List<WALSegment> getSegmentsPast(Long logMaxDurationMs) {
    long now = System.currentTimeMillis();
    List<WALSegment> markedForDeletion = new ArrayList<>();
    List<WALSegment> sortedSavedSegments = wal.sortedSavedSegments;
    for (WALSegment sortedSavedSegment : sortedSavedSegments) {
        if (timeElaspedSince(now, sortedSavedSegment.getLastLogEntryTimestamp()) > logMaxDurationMs) {
            markedForDeletion.add(sortedSavedSegment);
        }
    }
    return markedForDeletion;
}


private long timeElaspedSince(long now, long lastLogEntryTimestamp) {
    return now - lastLogEntryTimestamp;
}


例子

•所有共识算法(例如Zookeeper和RAFT)中的日志实现均实现基于快照的日志清理•Kafka中的存储实现遵循基于时间的日志清理

java达人

ID:drjava

(长按或扫码识别)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值