RocketMQ 笔记3 IndexFile

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.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

猜你喜欢

转载自blog.csdn.net/u013038630/article/details/80336255