除了通过通常的指定Topic进行消息消费外,rocketMQ还提供了根据Key进行查询的功能。该查询是通过store目录中的index子目录中的indexFile进行索引实现的快速查询。
1.索引条目结构
每个Broker中会包含一组indexFile,每个indexFile都是以一个时间戳
命名的(这个indexFile被创建时的时间戳)。每个indexFile文件由三部分构成:indexHeader,slots槽,indexes索引数据。每个IndexFile文件中包含500W个slot槽。而每个slot槽又可能会挂载很多的index索引单元。
indexHeader固定40个字节,其中存放着如下数据:
- beginTimestamp:该indexFile种第一条消息的存储时间
- endTimestamp:该indexFile种最后一条消息存储时间
- beginPhyoffset:该indexFile种第一条消息在commitlog种的偏移量commitlog offset
- endPhyoffset:该indexFile中最后一条消息在commitlog中的偏移量commitlog offset
- hashSlotCount:已经填充有index的slot数量
- indexCount:该indexFile中包含的索引单元个数
indexFile中最复杂的是Slots与Indexes间的关系。在实际存储时,Indexes是在Slots后面的,但为了便于理解,将他们的关系展示为以下形式:
key的hash值 %500W
的结果即为槽位,然后将该slot值修改为该index索引单元的indexNo,根据这个indexNo可以计算出该index单元在indexFile中的位置。由于取模结果的重复率还是很高的,为了解决这个问题,在每个Index索引单元中增加了preIndexNo,用于指定该slot种当前index索引单元的前一个index索引单元。相当于一个链表。
indexNo是一个在indexFile种的流水号,从0开始一次递增。即在一个indexFile中所有的indexNo是依次递增的。indexNo在index索引单元中是没有体现的,其是通过indexes中依次数出来的。
2.indexFile的创建
indexFile的文件名为当前文件被创建时的时间戳。
根据业务key进行查询时,查询条件除了key之外,还需要指定一个要查询的时间戳,表示要查询不大于该时间戳的最新的消息,即查询指定时间戳之前存储的最新消息。这个时间戳文件名可以简化拆线呢,提高查询效率。
index文件是何时创建的?
- 当第一条带key的消息发送来后,系统发现没有indexFile,此时会创建第一个indexFile文件。
- 当第一个indexFile中挂载的index索引单元数量超出2000W个时,会创建新的indexFile。当带key的消息发送到来后,系统会找到最新的indexFile,并从其indexHeader的最后4字节中读取到indexCount。
若indexCount>=2000W时,会创建新的indexFile
3.查询流程
当消费者通过业务key来查询相应的消息时,其需要经过一个相对比较复杂的查询流程。
具体查询流程如下: