直接顺序写的形式存储,每个文件设定固定大小,默认是1G即: 1073741824 bytes. 写满一个文件后,新开一个文件写入。文件名就是其存储的起始消息偏移量。
官方描述如下:
CommitLog:消息主体以及元数据的存储主体,存储Producer端写入的消息主体内容,消息内容不是定长的。单个文件大小默认1G ,文件名长度为20位,左边补零,剩余为起始偏移量,比如00000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。消息主要是顺序写入日志文件,当文件满了,写入下一个文件;
当给定一个偏移量,要查找某条消息时,只需在所有的commitlog文件中,根据其名字即可知道偏移的数据信息是否存在其中,即相当于可基于文件实现一个二分查找,实际上rocketmq实现得更简洁,直接一次性查找即可定位:
// org.apache.rocketmq.store.CommitLog#getData public SelectMappedBufferResult getData(final long offset, final boolean returnFirstOnNotFound) { int mappedFileSize = this.defaultMessageStore.getMessageStoreConfig().getMappedFileSizeCommitLog(); // 1. 先在所有commitlog文件中查找到对应所在的 commitlog 分片文件 MappedFile mappedFile = this.mappedFileQueue.findMappedFileByOffset(offset, returnFirstOnNotFound); if (mappedFile != null) { // 再从该分片文件中,移动余数的大小偏移,即可定位到要查找的消息记录了 int pos = (int) (offset % mappedFileSize); SelectMappedBufferResult result = mappedFile.selectMappedBuffer(pos); return result; } return null; } // 查找偏移所在commitlog文件的实现方式: // org.apache.rocketmq.store.MappedFileQueue#findMappedFileByOffset(long, boolean) // firstMappedFile.getFileFromOffset() / this.mappedFileSize 代表了第一条记录所处的文件位置编号 // offset / this.mappedFileSize 代表当前offset所处的文件编号 // 那么,两个编号相减就是当前offset对应的文件编号,因为第一个文件编号的相对位置是0 // 但有个前提:就是每个文件存储的大小必须是真实的对应的 offset 大小之差,而实际上consumeQueue根本无法确定它存了多少offset // 也就是说,只要文件定长,offset用于定位 commitlog文件就是合理的 int index = (int) ((offset / this.mappedFileSize) - (firstMappedFile.getFileFromOffset() / this.mappedFileSize)); MappedFile targetFile = null; try { // 所以,此处可以找到 commitlog 文件对应的 mappedFile targetFile = this.mappedFiles.get(index); } catch (Exception ignored) { } if (targetFile != null && offset >= targetFile.getFileFromOffset() && offset < targetFile.getFileFromOffset() + this.mappedFileSize) { return targetFile; } // 如果快速查找失败,则退回到遍历方式, 使用O(n)的复杂度再查找一次 for (MappedFile tmpMappedFile : this.mappedFiles) { if (offset >= tmpMappedFile.getFileFromOffset() && offset < tmpMappedFile.getFileFromOffset() + this.mappedFileSize) { return tmpMappedFile; } }