深入理解 StarRocks 的元數(shù)據(jù)管理
背景
最近在排查 starrocks 線上的一個(gè)告警日志:
圖片
每隔一段時(shí)間都會(huì)打印 base-table 也就是物化視圖的基表被刪除了,但其實(shí)表還在,也沒(méi)人去刪除;我們就懷疑是否真的表被刪除了(可能是 bug)。
與此同時(shí)還有物化視圖 inactive 的日志,也懷疑如果視圖是 inactive 之后會(huì)導(dǎo)致業(yè)務(wù)使用有問(wèn)題。
為了確認(rèn)這個(gè)日志是否對(duì)使用影響,就得需要搞清楚它出現(xiàn)的原因;于是我就著手從日志打印的地方開(kāi)始排查。
問(wèn)題排查
圖片
從這個(gè)代碼可以看出,是在查詢(xún)表的信息的時(shí)候沒(méi)有查到,從而導(dǎo)致日志打印 base-table 被 dropped 了。
而我查詢(xún)了幾天的 drop table 的日志,依然沒(méi)有找到可能是程序 bug 導(dǎo)致被刪除的痕跡。
好在 starrocks 的日志打印非常詳細(xì),包含了線程名稱(chēng)、類(lèi)+方法名稱(chēng),還有具體的代碼函數(shù),很容易就定位日志輸出的地方。
元數(shù)據(jù)
只是為何會(huì)調(diào)用到這里還需要閱讀源碼從而找到原因,在開(kāi)始之前需要先了解一下 starrocks 元數(shù)據(jù)的一些基本概念。
其實(shí)在這篇文章:StarRocks 元數(shù)據(jù)管理及 FE 高可用機(jī)制中已經(jīng)有全面的介紹,只是這篇文章有點(diǎn)早了,和現(xiàn)在最新的代碼不太匹配。
在 StarRocks 元數(shù)據(jù)中會(huì)保存 Database、Table 等信息。
這些數(shù)據(jù)定期保存在 fe/meta 目錄中。
圖片
StarRocks 對(duì)元數(shù)據(jù)的每一次操作(增刪改查數(shù)據(jù)庫(kù)、表、物化視圖)都會(huì)生成 editLog 的操作日志。
圖片
新建數(shù)據(jù)庫(kù)、修改表名稱(chēng)等。
當(dāng) StarRocks 的 FE 集群部署時(shí),會(huì)由 leader 的 FE 啟動(dòng)一個(gè) checkpoint 線程,定時(shí)掃描當(dāng)前的元數(shù)據(jù)是否需要生成一個(gè) image.${JournalId} 的文件。
圖片
其實(shí)就是判斷當(dāng)前日志數(shù)量是否達(dá)到上限(默認(rèn)是 5w)生成一次。
具體的流程如下:
圖片
- 判斷當(dāng)前是否需要將日志生成 image
- 加載當(dāng)前 image 里的元數(shù)據(jù)到內(nèi)存
- 從 bdb 中讀取最新的 Journal,然后進(jìn)行重放(replay):其實(shí)就是更新剛才加載到內(nèi)存中的元數(shù)據(jù)。
- 基于內(nèi)存中的元數(shù)據(jù)重新生成一份 image 文件
- 刪除歷史的 image 文件
- 將生成的 image 文件名稱(chēng)通知 FE 的 follower 節(jié)點(diǎn),讓他們下載到本地,從而可以實(shí)現(xiàn) image 同步。
圖片
通知 follower 下載 image。
元數(shù)據(jù)同步流程
完整的流程圖如下圖:
圖片
在這個(gè)流程圖有一個(gè)關(guān)鍵 loadImage 流程:
圖片
他會(huì)讀取 image 這個(gè)文件里的數(shù)據(jù),然后反序列化后加載到內(nèi)存里,主要就是恢復(fù)數(shù)據(jù)庫(kù)和表。
還會(huì)對(duì)每個(gè)表調(diào)用一次 onReload() 函數(shù),而這個(gè)函數(shù)會(huì)只 MV(MATERIALIZED VIEWS) 生效。
這個(gè)函數(shù)正好就是在文初提到的這個(gè)函數(shù) com.starrocks.catalog.MaterializedView#onReloadImpl:
圖片
從他的實(shí)現(xiàn)來(lái)看就是判斷視圖所依賴(lài)的基表是否存在,如果有一個(gè)不存在就會(huì)將當(dāng)前基表置為 inactive。
如果碰到視圖的基表也是視圖,那就遞歸再 reload 一次。
復(fù)現(xiàn)問(wèn)題
既然知曉了這個(gè)加載流程,再結(jié)合源碼應(yīng)該不難看出這里的問(wèn)題所在了。
圖片
從這里的加載數(shù)據(jù)庫(kù)可以看出端倪,如果我的視圖和基表不在同一個(gè)數(shù)據(jù)庫(kù)里,此時(shí)先加載視圖是不是就會(huì)出現(xiàn)問(wèn)題?
加載視圖的時(shí)候會(huì)判斷基表是否存在,而此時(shí)基表所在的數(shù)據(jù)庫(kù)還沒(méi)加載到內(nèi)存里,自然就會(huì)查詢(xún)不到從而出現(xiàn)那個(gè)日志。
我之前一直在本地模擬,因?yàn)槎际窃谕粋€(gè)數(shù)據(jù)庫(kù)里的基表和視圖,所以一直不能復(fù)現(xiàn)。
只要將基表和視圖分開(kāi)在不同的數(shù)據(jù)庫(kù)中,讓視圖先于數(shù)據(jù)庫(kù)前加載就會(huì)觸發(fā)這個(gè)日志。
修復(fù)問(wèn)題
要修復(fù)這個(gè)問(wèn)題也很簡(jiǎn)單,只要等到所有的數(shù)據(jù)庫(kù)都表都加載完畢后再去 reload 物化視圖就可以了。
當(dāng)我回到 main 分支準(zhǔn)備著手修改時(shí),發(fā)現(xiàn)這個(gè)問(wèn)題已經(jīng)被修復(fù)了:https://github.com/StarRocks/starrocks/pull/51002
圖片
修復(fù)過(guò)程也很簡(jiǎn)單,就是 reload 時(shí)跳過(guò)了 MV,等到所有的數(shù)據(jù)都加載完之后會(huì)在 com.starrocks.server.GlobalStateMgr#postLoadImage 手動(dòng)加載 MV。
圖片
這個(gè) PR 修復(fù)的問(wèn)題也是我一開(kāi)始提到的,會(huì)打印許多令人誤解的日志。
到這里就可以解釋文章開(kāi)頭的那個(gè)問(wèn)題了:打印的這個(gè) base-table 被刪除的日志對(duì)業(yè)務(wù)來(lái)說(shuō)沒(méi)有影響,只是一個(gè) bug 導(dǎo)致出現(xiàn)了這個(gè)日志。
額外提一句,這個(gè)日志也比較迷,沒(méi)有打印數(shù)據(jù)庫(kù)名稱(chēng),如果有數(shù)據(jù)庫(kù)名稱(chēng)的話可能會(huì)更快定位到這個(gè)問(wèn)題。
參考文章:
- https://xie.infoq.cn/article/6f2f9f56916f0eb2fdb6b001a
- https://github.com/StarRocks/starrocks/pull/51002