rocketmq消息索引是怎麼做的

rocketmq消息索引是怎麼做的

參考文章

  • 形象的圖示舉例很棒:https://kunzhao.org/docs/rocketmq/rocketmq-message-indexing-flow/

  • 寫簡單代碼解析rocketmq的消息索引文件:https://juejin.im/post/5eaa6add6fb9a04359028457

在使用rmq的過程中覺得他的消息檢索很方便,像一個db一樣,很好奇它在數據量這麼大的情況下,是怎麼做到根據messageId、message-key、時間範圍這些因素去快速的搜索的。

首先根據messageId原理比較簡單,messageId裏包含了broker地址信息和在commitLog裏的offset,通過這兩個信息我們就可以去對應的broker裏去查找CommitLog的具體offset就可以拿到消息了。解析方法在org.apache.rocketmq.common.message.MessageDecoder#decodeMessageId。

by the way,這種在id裏存儲信息的做法在elasticsearch裏也有做,默認生成的doc_id是有包含node信息的,這種特性在排查問題的時候還是很有用的。

下面主要介紹message-key和時間範圍搜索是怎麼做到的。

消息索引服務

有專門的索引文件,存儲在 store/index下,一個索引文件相當於一個很大的hash表,整個文件結構有3部分,這3部分從頭至尾依次排開

  1. index_header:存儲了當前索引文件的一些全局信息,比如存儲的消息的最大最小offset、store的時間的最大最小值,已使用的hashslot數量,已使用的index item數量
  2. hash slot list:hash槽列表,由五百萬個hash槽組成,每個槽存儲“產生hash碰撞”的鏈表的頭結點
  3. index item list:真正的索引信息,由一個個的index-item(兩千萬個位置)組成,每個index-item存儲key的hash值、物理偏移量、時間戳以及一個指針(與其發生hash碰撞的下一個index-item的地址)

作爲索引文件來說,功能主要有兩個,一個是put、一個是get

put——加入新消息

前幾步都是經典的hash取模操作

  1. 根據消息的key計算hash值

  2. hash對500w取模,找到對應的hash-slot

  3. 拿到找到的hash-slot的已有的值a(指的是最近在這個slot發生hash碰撞的index-item的地址,默認是0),順序從index-item-list找一個位置b,將消息的hash值、物理偏移量、時間戳和剛剛拿到的最新發生hash碰撞的index-item的地址a組成一個新的index-item放到位置b。所以這裏可以看到rmq在消息索引這裏解決hash衝突的方式也是一種拉鍊法,而且個人覺得這個拉鍊法也是使用頭插法處理新來的entry的

get——根據key查詢消息

先根據查詢的時間戳範圍,過濾不在此範圍內的文件,然後在過濾後的每個文件裏執行查找操作

  1. 計算查詢的key的hash值
  2. hash值取模,找到對應的hash-slot
  3. 從hash-slot拿到的第一個index-item開始遍歷這個鏈表,遇到匹配的消息就收集他們的偏移量,最後統一去commitLog文件取出消息。注意,因爲這裏我們的index-item沒有存儲具體的key值,只存了key值的hash值,所以沒有辦法像Java的hashMap那樣在發生hash衝突的鏈表上使用key值的具體內容一個一個的equals尋找真正需要的值,只能按照時間範圍和已有的hash值對比拿到一系列的msg的offset,然後去commitLog裏去查,查完之後再進行過濾。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章