概述
CommitLog中存储了消息的完整信息,所有topic的消息都存储在一个CommitLog中。下面讲一下CommitLog的存储格式和存储过程
存储文件信息
CommtLog的存储其实是分多层的,CommitLog -> MappedFileQueue -> MappedFile,其中真正存储数据的是MappedFile。CommitLog存储目录为$HOME\store\commitlog${fileName},每个文件大小为1G,不满则填充空白。每个文件名字长度为20,具体命名为文件偏移量,左边补0。如第一个文件为00000000000000000000,第二个文件为00000000001073741824。
CommitLog消息格式
关键属性
MappedFileQueue
CommitLog存储在多个文件中,多个文件用MappedFileQueue来维护。MappedFileQueue是MappedFile的集合,用来管理MappedFile队列,提供了查找、定位、删除等功能。其属性mappedFiles为CopyOnWriteArrayList。
下面看具体方法。
checkSelf()自检
public void checkSelf() {
if (!this.mappedFiles.isEmpty()) {
Iterator<MappedFile> iterator = mappedFiles.iterator();
MappedFile pre = null;
// 遍历所有mappedFile
while (iterator.hasNext()) {
MappedFile cur = iterator.next();
if (pre != null) {
// 校验相邻两个文件offset差值是否为文件大小
if (cur.getFileFromOffset() - pre.getFileFromOffset() != this.mappedFileSize) {
LOG_ERROR.error("[BUG]The mappedFile queue's data is damaged, the adjacent mappedFile's offset don't match. pre file {}, cur file {}",
pre.getFileName(), cur.getFileName());
}
}
pre = cur;
}
}
}
getMappedFileByTime()时间戳查找文件
按文件最后更新时间查找。这里的文件生成后不会再更新,所以队列中文件的最后修改时间是递增的。
truncateDirtyFiles()清理脏文件
清理大于指定offset的文件
删除过期文件
按照不同方式删除,看源码即可
getLastMappedFile(final long startOffset, boolean needCreate)获取最后一个文件
/**
* 三种情况
* 1、mappedFiles为空,如果需要则创建新文件
* 2、如果最后一个文件满了,如果需要则创建新文件
* 3、返回最后一个文件
*/
public MappedFile getLastMappedFile(final long startOffset, boolean needCreate) {
long createOffset = -1;
MappedFile mappedFileLast = getLastMappedFile();
// mappedFiles为空
if (mappedFileLast == null) {
createOffset = startOffset - (startOffset % this.mappedFileSize);
}
// 最后一个文件满了
if (mappedFileLast != null && mappedFileLast.isFull()) {
createOffset = mappedFileLast.getFileFromOffset() + this.mappedFileSize;
}
// 创建文件
if (createOffset != -1 && needCreate) {
String nextFilePath = this.storePath + File.separator + UtilAll.offset2FileName(createOffset);
String nextNextFilePath = this.storePath + File.separator
+ UtilAll.offset2FileName(createOffset + this.mappedFileSize);
MappedFile mappedFile = null;
if (this.allocateMappedFileService != null) {
mappedFile = this.allocateMappedFileService.putRequestAndReturnMappedFile(nextFilePath,
nextNextFilePath, this.mappedFileSize);
} else {
try {
mappedFile = new MappedFile(nextFilePath, this.mappedFileSize);
} catch (IOException e) {
log.error("create mappedFile exception", e);
}
}
if (mappedFile != null) {
if (this.mappedFiles.isEmpty()) {
mappedFile.setFirstCreateInQueue(true);
}
this.mappedFiles.add(mappedFile);
}
return mappedFile;
}
return mappedFileLast;
}
flush(int flushLeastPages)刷盘
public boolean flush(final int flushLeastPages) {
boolean result = true;
// 上一次刷盘位置获取文件
MappedFile mappedFile = this.findMappedFileByOffset(this.flushedWhere, this.flushedWhere == 0);
if (mappedFile != null) {
long tmpTimeStamp = mappedFile.getStoreTimestamp();
// 交由mappedFile刷盘
int offset = mappedFile.flush(flushLeastPages);
long where = mappedFile.getFileFromOffset() + offset;
// false表示有刷盘操作,true表示无变化
result = where == this.flushedWhere;
this.flushedWhere = where;
if (0 == flushLeastPages) {
this.storeTimestamp = tmpTimeStamp;
}
}
return result;
}
commit(int commitLeastPages)提交数据
public boolean commit(final int commitLeastPages) {
boolean result = true;
// 上一次提交位置获取文件
MappedFile mappedFile = this.findMappedFileByOffset(this.committedWhere, this.committedWhere == 0);
if (mappedFile != null) {
// 交由mappedFile提交
int offset = mappedFile.commit(commitLeastPages);
long where = mappedFile.getFileFromOffset() + offset;
// false表示有提交操作,true表示无变化
result = where == this.committedWhere;
this.committedWhere = where;
}
return result;
}
其他查询相关不再详细陈述,具体看源码。
flushCommitLogService属性
刷盘服务,分为同步刷盘和异步刷盘,具体看配置。同步刷盘服务为GroupCommitService,异步刷盘服务为FlushRealTimeService,后面详细分析。
commitLogService属性
暂存数据提交服务,对应CommitRealTimeService,后面详细讲。
具体方法
flush
交由mappedFileQueue执行。
public long flush() {
this.mappedFileQueue.commit(0);