作者: Unmesh Joshi
译者: java达人
来源: https://martinfowler.com/articles/patterns-of-distributed-systems/
将日志拆分为多个较小的文件,而不是单个较大的文件,以便于操作。
单个日志文件可能会增长并在启动读取时成为性能瓶颈。需要定期清理较旧的日志,而对单个大文件执行清理操作是困难的
单个日志分为多个段。在到达指定的大小限制后滚动日志文件。
public Long writeEntry(WALEntry entry) {
maybeRoll();
return openSegment.writeEntry(entry);
}
private void maybeRoll() {
if (openSegment.
size() >= config.getMaxLogSize()) {
openSegment.flush();
sortedSavedSegments.add(openSegment);
long lastId = openSegment.getLastLogEntryId();
openSegment = WALSegment.open(lastId, config.getWalDir());
}
}
使用日志分段,需要一种简单的方法将日志逻辑偏移量(或日志序列号)映射到日志段文件。这可以通过两种方式完成:
•每个日志段名称都是由一些众所周知的前缀和基准偏移量(或日志序列号)生成的。
•每个日志序列号分为两部分,文件名和事务偏移量。
public static String createFileName(Long startIndex) {
return logPrefix + "_" + startIndex + logSuffix;
}
public static Long getBaseOffsetFromFileName(String fileName) {
String[] nameAndSuffix = fileName.split(logSuffix);
String[] prefixAndOffset = nameAndSuffix[0].split("_");
if (prefixAndOffset[0].equals(logPrefix))
return Long.parseLong(prefixAndOffset[1]);
return -1l;
}
利用此信息,读取操作分为两个步骤。对于给定的偏移量(或事务ID),将标识日志段,并从后续日志段中读取所有日志记录。
public List<WALEntry> readFrom(Long startIndex) {
List<WALSegment> segments = getAllSegmentsContainingLogGreaterThan(startIndex);
return readWalEntriesFrom(startIndex, segments);
}
private List<WALSegment> getAllSegmentsContainingLogGreaterThan(Long startIndex) {
List<WALSegment> segments = new ArrayList<>();
//Start from the last segment to the first segment with starting offset less than startIndex
//This will get all the segments which have log entries more than the startIndex
for (int i = sortedSavedSegments.size() - 1; i >= 0; i--) {
WALSegment walSegment = sortedSavedSegments.get(i);
segments.add(walSegment);
if (walSegment.getBaseOffset() <= startIndex) {
break; // break for the first segment with baseoffset less than startIndex
}
}
if (openSegment.getBaseOffset() <= startIndex) {
segments.add(openSegment);
}
return segments;
}
•所有共识实现(例如Zookeeper和RAFT)中的日志实现都使用日志分段。
•Kafka中的存储实现遵循日志分段。
•所有数据库,包括像Cassandra这样的nosql数据库,都基于一些预先配置的日志大小使用roll over 策略。