IndexFile
大小固定,文件名是创建时候的时间戳
IndexHeader(40字节) Slot Table(500w * 4字节) Index Linked List(4 * 500W * 20字节)
IndexHeader结构
beginTimestamp(8) 第一个索引消息保存broker的时间戳
endTimestamp(8) 最后一个索引消息保存broker的时间戳
beginPhyOffset(8) 第一个索引消息在commitLog的偏移地址
endPhyOffset(8) 最后一个索引消息在commitlog的偏移地址
hashSlotCount(4) 从0开始,计数,记录Slot Table使用个数
indexCount(4) 从1开始,计数,记录Index Linked List使用个数
Slot Table里面保存的是 Index Linked List的索引,最后一个index,例如从空开始连续存3个keyhash一样的
Slot Table中某个(keyhash % 500w 位置处)slot保存3,代表从链表的第三个开始找
Index Linked List 第3个的slotValue保存2,第2个的slotValue保存1,第1个的slotValue保存0,就这样形成了一个链表
Index Linked List每个节点20字节,结构为
keyHash(4) topic+key的hash值
phyOffset(8) 消息在commitLog中的偏移地址
timeDiff(4) 消息的保存时间戳减去IndexHeader的beginTimestamp
slotValue 上一个相同keyHash的节点在Index Linked List的位置
1.写索引过程
org.apache.rocketmq.store.DefaultMessageStore.CommitLogDispatcherBuildIndex.dispatch(DispatchRequest)
org.apache.rocketmq.store.index.IndexService.buildIndex(DispatchRequest)
拿到最后一个索引文件,可能新建新的索引文件,新建索引文件时候,起一个新线程flush上一个索引文件
先写UniqKey,然后keys
具体文件写索引过程
org.apache.rocketmq.broker.processor.QueryMessageProcessor.processRequest(ChannelHandlerContext, RemotingCommand)
broker处理查询消息
org.apache.rocketmq.store.DefaultMessageStore.queryMessage(String, String, int, long, long)
先调用indexService查询出所有符合条件的消息在commitLog的Offset
再使用commitLog根据offset拿到具体消息
org.apache.rocketmq.store.index.IndexService.queryOffset(String, String, int, long, long)
便利所有的索引文件,查询
org.apache.rocketmq.store.index.IndexFile.selectPhyOffset(List<Long>, String, int, long, long, boolean)
根据keyhash找到table位置,读出来就是最后一个索引记录,然后依次往前读,校验时间和keyhash,就拿到这个文件中的所有
符合条件的消息offset
大小固定,文件名是创建时候的时间戳
IndexHeader(40字节) Slot Table(500w * 4字节) Index Linked List(4 * 500W * 20字节)
IndexHeader结构
beginTimestamp(8) 第一个索引消息保存broker的时间戳
endTimestamp(8) 最后一个索引消息保存broker的时间戳
beginPhyOffset(8) 第一个索引消息在commitLog的偏移地址
endPhyOffset(8) 最后一个索引消息在commitlog的偏移地址
hashSlotCount(4) 从0开始,计数,记录Slot Table使用个数
indexCount(4) 从1开始,计数,记录Index Linked List使用个数
Slot Table里面保存的是 Index Linked List的索引,最后一个index,例如从空开始连续存3个keyhash一样的
Slot Table中某个(keyhash % 500w 位置处)slot保存3,代表从链表的第三个开始找
Index Linked List 第3个的slotValue保存2,第2个的slotValue保存1,第1个的slotValue保存0,就这样形成了一个链表
Index Linked List每个节点20字节,结构为
keyHash(4) topic+key的hash值
phyOffset(8) 消息在commitLog中的偏移地址
timeDiff(4) 消息的保存时间戳减去IndexHeader的beginTimestamp
slotValue 上一个相同keyHash的节点在Index Linked List的位置
1.写索引过程
org.apache.rocketmq.store.DefaultMessageStore.CommitLogDispatcherBuildIndex.dispatch(DispatchRequest)
org.apache.rocketmq.store.index.IndexService.buildIndex(DispatchRequest)
拿到最后一个索引文件,可能新建新的索引文件,新建索引文件时候,起一个新线程flush上一个索引文件
先写UniqKey,然后keys
具体文件写索引过程
org.apache.rocketmq.store.index.IndexFile.putKey(String, long, long)
public boolean putKey(final String key, final long phyOffset, final long storeTimestamp) { if (this.indexHeader.getIndexCount() < this.indexNum) {//未写满 int keyHash = indexKeyHashMethod(key); int slotPos = keyHash % this.hashSlotNum;//找到table位置 int absSlotPos = IndexHeader.INDEX_HEADER_SIZE + slotPos * hashSlotSize;//table位置的偏移地址 FileLock fileLock = null; try { // fileLock = this.fileChannel.lock(absSlotPos, hashSlotSize, // false); int slotValue = this.mappedByteBuffer.getInt(absSlotPos);//取得table位置处的值,也就是后面indexArray的索引 if (slotValue <= invalidIndex || slotValue > this.indexHeader.getIndexCount()) { slotValue = invalidIndex;//越界情况为0 } long timeDiff = storeTimestamp - this.indexHeader.getBeginTimestamp();//时间戳差 timeDiff = timeDiff / 1000; if (this.indexHeader.getBeginTimestamp() <= 0) { timeDiff = 0; } else if (timeDiff > Integer.MAX_VALUE) { timeDiff = Integer.MAX_VALUE; } else if (timeDiff < 0) { timeDiff = 0; } int absIndexPos = IndexHeader.INDEX_HEADER_SIZE + this.hashSlotNum * hashSlotSize + this.indexHeader.getIndexCount() * indexSize;//计算后面indexArray的偏移地址 this.mappedByteBuffer.putInt(absIndexPos, keyHash); this.mappedByteBuffer.putLong(absIndexPos + 4, phyOffset); this.mappedByteBuffer.putInt(absIndexPos + 4 + 8, (int) timeDiff); this.mappedByteBuffer.putInt(absIndexPos + 4 + 8 + 4, slotValue);//写入四个信息 this.mappedByteBuffer.putInt(absSlotPos, this.indexHeader.getIndexCount());//把最后一个array的索引写入table if (this.indexHeader.getIndexCount() <= 1) { this.indexHeader.setBeginPhyOffset(phyOffset); this.indexHeader.setBeginTimestamp(storeTimestamp); } this.indexHeader.incHashSlotCount();//写header的一些信息 this.indexHeader.incIndexCount(); this.indexHeader.setEndPhyOffset(phyOffset); this.indexHeader.setEndTimestamp(storeTimestamp); return true; } catch (Exception e) { log.error("putKey exception, Key: " + key + " KeyHashCode: " + key.hashCode(), e); } finally { if (fileLock != null) { try { fileLock.release(); } catch (IOException e) { log.error("Failed to release the lock", e); } } } } else { log.warn("Over index file capacity: index count = " + this.indexHeader.getIndexCount() + "; index max num = " + this.indexNum); } return false; }2.根据索引查询消息过程
org.apache.rocketmq.broker.processor.QueryMessageProcessor.processRequest(ChannelHandlerContext, RemotingCommand)
broker处理查询消息
org.apache.rocketmq.store.DefaultMessageStore.queryMessage(String, String, int, long, long)
先调用indexService查询出所有符合条件的消息在commitLog的Offset
再使用commitLog根据offset拿到具体消息
org.apache.rocketmq.store.index.IndexService.queryOffset(String, String, int, long, long)
便利所有的索引文件,查询
org.apache.rocketmq.store.index.IndexFile.selectPhyOffset(List<Long>, String, int, long, long, boolean)
根据keyhash找到table位置,读出来就是最后一个索引记录,然后依次往前读,校验时间和keyhash,就拿到这个文件中的所有
符合条件的消息offset