時(shí)延降低 50%,小紅書(shū)圖數(shù)據(jù)庫(kù)如何實(shí)現(xiàn)多跳查詢性能大幅提升
多跳查詢?yōu)槠髽I(yè)提供了深入的數(shù)據(jù)洞察和分析能力,它在小紅書(shū)眾多在線業(yè)務(wù)中扮演重要的角色。然而,這類查詢往往很難滿足穩(wěn)定的 P99 時(shí)延要求。小紅書(shū)基礎(chǔ)架構(gòu)存儲(chǔ)團(tuán)隊(duì)針對(duì)這一挑戰(zhàn),基于大規(guī)模并行處理(MPP)的理念,開(kāi)發(fā)了一種圖數(shù)據(jù)庫(kù)上的分布式并行查詢框架,成功將多跳查詢的時(shí)延降低了 50% 以上,尤其是使 3 跳查詢?cè)谠诰€場(chǎng)景從不能用到落地,極大地增強(qiáng)了在線業(yè)務(wù)的數(shù)據(jù)處理能力。
本文核心貢獻(xiàn)在于:團(tuán)隊(duì)提出了一種從框架層面優(yōu)化多跳查詢時(shí)延的方案,在業(yè)務(wù)上使在線場(chǎng)景中使用多跳查詢成為可能,在技術(shù)上實(shí)現(xiàn)了圖數(shù)據(jù)庫(kù)查詢的框架級(jí)優(yōu)化。全文將從以下幾個(gè)方面依次展開(kāi):
- 介紹小紅書(shū)使用圖數(shù)據(jù)庫(kù)的背景,并分析多跳查詢?cè)趯?shí)際業(yè)務(wù)中因時(shí)延高而受限的現(xiàn)狀(需求是什么)
- 深入探討 REDgraph 架構(gòu),揭示原有查詢模式的不足和可優(yōu)化點(diǎn)(存在的問(wèn)題)
- 詳細(xì)闡述優(yōu)化原查詢模式的方案,并提供部分實(shí)現(xiàn)細(xì)節(jié)(改進(jìn)方案)
- 通過(guò)一系列性能測(cè)試,驗(yàn)證優(yōu)化措施的有效性(改進(jìn)后效果)
本方案為具有復(fù)雜查詢需求的在線存儲(chǔ)產(chǎn)品提供了優(yōu)化思路,歡迎業(yè)界同行深入探討。
同時(shí),作者再興曾在「DataFunCon 2024·上海站」分享過(guò)本議題,感興趣的同學(xué)歡迎點(diǎn)擊“閱讀原文”,回看完整錄播視頻。
一、背景
1.1 圖數(shù)據(jù)庫(kù)在小紅書(shū)的使用場(chǎng)景
小紅書(shū)是一個(gè)以社區(qū)屬性為主的產(chǎn)品,覆蓋多個(gè)領(lǐng)域,鼓勵(lì)用戶通過(guò)圖文、短視頻、直播等形式記錄和分享生活點(diǎn)滴。在社交領(lǐng)域中,我們存在多種實(shí)體,如用戶、筆記、商品等,它們之間構(gòu)成了復(fù)雜的關(guān)系網(wǎng)絡(luò)。為高效處理這些實(shí)體間的一跳查詢,小紅書(shū)自研了圖存儲(chǔ)系統(tǒng) REDtao,滿足極致性能的需求。
(參見(jiàn):小紅書(shū)如何應(yīng)對(duì)萬(wàn)億級(jí)社交網(wǎng)絡(luò)關(guān)系挑戰(zhàn)?圖存儲(chǔ)系統(tǒng) REDtao 來(lái)了!)
面對(duì)更為復(fù)雜的多跳查詢場(chǎng)景,我們自研了圖數(shù)據(jù)庫(kù)系統(tǒng) REDgraph,將多跳查詢的需求應(yīng)用于小紅書(shū)多個(gè)業(yè)務(wù)領(lǐng)域,包括但不限于:
- 社區(qū)推薦:利用用戶間的關(guān)系鏈和分享鏈,為用戶推薦可能感興趣的好友、筆記和視頻。這類推薦機(jī)制通常涉及多于一跳的復(fù)雜關(guān)系。
- 風(fēng)控場(chǎng)景:通過(guò)分析用戶和設(shè)備的行為模式,識(shí)別可能的欺詐行為(如惡意薅羊毛),從而保護(hù)平臺(tái)免受濫用和作弊行為的影響。
- 電商場(chǎng)景:構(gòu)建商品與商品、商品與品牌之間的關(guān)聯(lián)模型,優(yōu)化商品分類和推薦,從而提升用戶的購(gòu)物體驗(yàn)。
在傳統(tǒng)的 SQL 產(chǎn)品(如 MySQL)中,想實(shí)現(xiàn)這些多跳查詢,通常需要在一個(gè)查詢語(yǔ)句中寫多個(gè) JOIN,這樣的性能無(wú)疑是較差的。若想利用鍵值存儲(chǔ) KV 產(chǎn)品實(shí)現(xiàn),則需要分多次發(fā)送 get 請(qǐng)求,并自行處理中間結(jié)果,實(shí)現(xiàn)過(guò)程也較為麻煩。
相比之下,圖數(shù)據(jù)庫(kù)的設(shè)計(jì)理念為處理這類查詢提供了天然優(yōu)勢(shì)。在圖數(shù)據(jù)庫(kù)中,數(shù)據(jù)表被抽象為頂點(diǎn),表之間的關(guān)系被抽象為邊,并且邊作為一等公民被存儲(chǔ)和處理。這樣一來(lái),執(zhí)行 n 度關(guān)系查詢只需查詢 n 次邊表,大大簡(jiǎn)化查詢過(guò)程,并提高了效率。
1.2 業(yè)務(wù)上面臨的困境
小紅書(shū)在社交、風(fēng)控及離線任務(wù)調(diào)度等場(chǎng)景中均采用了圖數(shù)據(jù)庫(kù),然而在實(shí)際應(yīng)用過(guò)程中遇到了一些挑戰(zhàn)。
場(chǎng)景一:社交推薦
在社交推薦中,我們希望能夠及時(shí)地將用戶可能感興趣的好友或內(nèi)容推薦給他們。例如,如果用戶 A 關(guān)注了用戶 B,而用戶 B 點(diǎn)贊了筆記 C,那么用戶 D(也點(diǎn)贊了筆記 C)就可能成為用戶 A 的潛在好友,使小紅書(shū)的好友社區(qū)建立更豐富的連接。
業(yè)務(wù)當(dāng)然可以使用離線任務(wù)分析,基于分析結(jié)果進(jìn)行推薦,但社交圖譜是無(wú)時(shí)無(wú)刻不在變化,基于離線分析做出的推薦往往是滯后的。如果推薦得更及時(shí),能更好地抓住一些潛在的用戶關(guān)系,建立更豐富完善的社交圖譜,賦能其他業(yè)務(wù)(如:社區(qū)興趣小組,電商商品推薦)。
業(yè)務(wù)希望能即時(shí)向用戶推送可能感興趣的 “好友” 或 “內(nèi)容”,如果能即時(shí)完成此推薦,則能有效優(yōu)化用戶使用體驗(yàn),提升留存率。然而,由于先前 REDgraph 在某些方面的能力尚未完善,導(dǎo)致三跳時(shí)延比較高,所以業(yè)務(wù)一直只采用了一跳和兩跳查詢。
場(chǎng)景二:社區(qū)生態(tài)與風(fēng)險(xiǎn)控制
小紅書(shū)致力于促進(jìn)社區(qū)生態(tài)的健康發(fā)展,對(duì)優(yōu)質(zhì)內(nèi)容創(chuàng)作者提供獎(jiǎng)勵(lì)。然而,這也吸引了一些作弊用戶想薅羊毛。例如,作弊用戶可能會(huì)通過(guò)組織點(diǎn)贊來(lái)提升低質(zhì)量筆記的排名,將低質(zhì)筆記偽造成優(yōu)質(zhì)筆記以賺取獎(jiǎng)勵(lì)。
風(fēng)控業(yè)務(wù)需要對(duì)這種行為予以識(shí)別并防范,借助圖數(shù)據(jù)庫(kù)的多跳查詢,我們構(gòu)建出一個(gè)包含用戶和筆記為頂點(diǎn)、點(diǎn)贊為邊的復(fù)雜關(guān)系圖(“用戶->筆記-> ... ->用戶->筆記“)。隨后,對(duì)每篇筆記查詢其多度關(guān)系(筆記 -> 用戶 -> 筆記 -> 用戶)上作弊用戶的比例,若比例高于一定閾值,把筆記打上作弊標(biāo)簽,系統(tǒng)便不對(duì)作弊用戶和作弊筆記發(fā)放獎(jiǎng)勵(lì)。
打標(biāo)行為往往是實(shí)時(shí)消費(fèi)消息隊(duì)列去查詢圖數(shù)據(jù)庫(kù),如果查詢動(dòng)作本身比較慢,則會(huì)造成整體消費(fèi)積壓。例如,如果一個(gè)查詢?nèi)蝿?wù)本應(yīng)在 12:00 執(zhí)行,但由于性能問(wèn)題直到 12:10 才開(kāi)始觸發(fā),那么在這十分鐘的延遲期間,一篇劣質(zhì)筆記已成為優(yōu)質(zhì)筆記,作者薅羊毛成功。等到發(fā)現(xiàn)這是作弊用戶時(shí),顯然「為時(shí)晚矣」,因?yàn)閾p失已經(jīng)造成了。
具體來(lái)說(shuō),社交推薦場(chǎng)景要求三跳的 P99 低于 50 毫秒,風(fēng)控場(chǎng)景則要求三跳的 P99 低于 200 毫秒,這是目前 REDgraph 所面臨的一大難題。那為何一至二跳可行,三跳及以上就難以實(shí)現(xiàn)呢?對(duì)此,我們基于圖數(shù)據(jù)庫(kù)與其他類型系統(tǒng)在工作負(fù)載的差異,做了一些難點(diǎn)與可行性分析。
1.3 難點(diǎn)與可行性分析
首先在并發(fā)方面,OLTP 的并發(fā)度很高,而 OLAP 則相對(duì)較低。圖的三跳查詢,服務(wù)的仍然是在線場(chǎng)景,其并發(fā)度也相對(duì)較高,這塊更貼近 OLTP 場(chǎng)景。
其次在計(jì)算復(fù)雜度方面,OLTP 場(chǎng)景中的查詢語(yǔ)句較為簡(jiǎn)單,包含一到兩個(gè) join 操作就算是較為復(fù)雜的情況了,因此,OLTP 的計(jì)算復(fù)雜度相對(duì)較低。OLAP 則是專門為計(jì)算設(shè)計(jì)的,因此其計(jì)算復(fù)雜度自然較高。圖的三跳查詢則介于 OLTP 和 OLAP 之間,它雖不像 OLAP 那樣需要執(zhí)行大量的計(jì)算,但其訪問(wèn)的數(shù)據(jù)量相對(duì)于 OLTP 來(lái)說(shuō)還是更可觀的,因此屬于中等復(fù)雜度。
第三,數(shù)據(jù)時(shí)效性方面,OLTP 對(duì)時(shí)效性的要求較高,必須基于最新的數(shù)據(jù)提供準(zhǔn)確且實(shí)時(shí)的響應(yīng)。而在 OLAP 場(chǎng)景中則沒(méi)有這么高的時(shí)效要求,早期的 OLAP 數(shù)據(jù)庫(kù)通常提供的是 T+1 的時(shí)效。圖的三跳查詢,由于我們服務(wù)的是在線場(chǎng)景,所以對(duì)時(shí)效性有一定的要求,但并不是非常高。使用一小時(shí)或 10 分鐘前的狀態(tài)進(jìn)行推薦,也不會(huì)產(chǎn)生過(guò)于嚴(yán)重的后果。因此,我們將其定義為中等時(shí)效性。
最后,查詢失敗代價(jià)方面。OLTP 一次查詢的成本較低,因此其失敗的代價(jià)也低;而 OLAP 由于需要消耗大量的計(jì)算資源,因此其失敗代價(jià)很高。圖查詢?cè)谶@塊,更像 OLTP 場(chǎng)景一些,能夠容忍一些失敗,但畢竟訪問(wèn)的數(shù)據(jù)量較大,在查一遍代價(jià)稍高,因此同樣歸屬到中等。
總結(jié)一下:圖的三跳查詢具備 OLTP 級(jí)別的并發(fā)度,卻又有比一般 OLTP 大得多的數(shù)據(jù)訪問(wèn)量和計(jì)算復(fù)雜度,所以比較難在在線場(chǎng)景中使用。好在其對(duì)數(shù)據(jù)時(shí)效性的要求沒(méi)那么高,也能容忍一些查詢失敗,所以我們能嘗試對(duì)其優(yōu)化。
此外正如上文指出的,在小紅書(shū)業(yè)務(wù)場(chǎng)景中,三跳查詢的首要目標(biāo)還是降低延遲。有些系統(tǒng)中會(huì)考慮犧牲一點(diǎn)時(shí)延來(lái)?yè)Q取吞吐的大幅提升,而這在小紅書(shū)業(yè)務(wù)上是不可接受的。如果吞吐上不去,還可以通過(guò)擴(kuò)大集群規(guī)模來(lái)作為兜底方案,而如果時(shí)延高則直接不能使用了。
二、原架構(gòu)問(wèn)題分析
2.1 REDgraph 架構(gòu)
REDgraph 的整體結(jié)構(gòu)如上圖所示,其與當(dāng)前較為流行的 NewSQL,如 TiDB 的架構(gòu)構(gòu)相似,采用了存算分離 + shared-nothing 的架構(gòu)。奇包含三類節(jié)點(diǎn):
- Meta 服務(wù):負(fù)責(zé)管理圖數(shù)據(jù)庫(kù)的元信息,包括數(shù)據(jù)模式(Schema)、用戶賬號(hào)和權(quán)限、存儲(chǔ)分片的位置信息、作業(yè)與后臺(tái)任務(wù)等;
- Graph 服務(wù):負(fù)責(zé)處理用戶的查詢請(qǐng)求,并做相應(yīng)的處理,涵蓋查詢的解析、校驗(yàn)、優(yōu)化、調(diào)度、執(zhí)行等環(huán)節(jié)。其本身是無(wú)狀態(tài)的,便于彈性擴(kuò)縮容;
- Storgae 服務(wù):負(fù)責(zé)數(shù)據(jù)的物理存儲(chǔ),其架構(gòu)分為三層。最上層是圖語(yǔ)義 API,將 API 請(qǐng)求轉(zhuǎn)換為對(duì) Graph 的鍵值(KV)操作;中間層采用 Raft 協(xié)議實(shí)現(xiàn)共識(shí)機(jī)制,確保數(shù)據(jù)副本的強(qiáng)一致性和高可用性;最底層是單機(jī)存儲(chǔ)引擎,使用 rocksdb 來(lái)執(zhí)行數(shù)據(jù)的增刪查等操作。
2.2 圖切分方式
圖切分的含義是,如果我們擁有一個(gè)巨大的圖,規(guī)模在百億到千億水平,應(yīng)該如何將其存儲(chǔ)在集群的各節(jié)點(diǎn)之中,即如何對(duì)其切分。在工業(yè)界中,主要存在兩種典型的切分策略:邊切分和點(diǎn)切分。
邊切分,以頂點(diǎn)為中心,這種切分策略的核心思想是每個(gè)頂點(diǎn)會(huì)根據(jù)其 ID 進(jìn)行哈希運(yùn)算,并將其路由到特定的分片上。每個(gè)頂點(diǎn)上的每條邊在磁盤中都會(huì)被存儲(chǔ)兩份,其中一份與起點(diǎn)位于同一分片,另一份則與終點(diǎn)位于同一分片。
點(diǎn)切分,與邊切分相對(duì)應(yīng),也就是以邊為中心做哈希打散,每個(gè)頂點(diǎn)會(huì)在集群內(nèi)保存多份。
這兩種切分方式各有利弊。邊切分的優(yōu)點(diǎn)在于每個(gè)頂點(diǎn)與其鄰居都保存在同一個(gè)分片中,因此當(dāng)需要查詢某個(gè)頂點(diǎn)的鄰居時(shí),其訪問(wèn)局部性極佳;其缺點(diǎn)在于容易負(fù)載不均,且由于節(jié)點(diǎn)分布的不均勻性,引發(fā)熱點(diǎn)問(wèn)題。點(diǎn)切分則恰恰相反,其優(yōu)點(diǎn)在于負(fù)載較為均衡,但缺點(diǎn)在于每個(gè)頂點(diǎn)會(huì)被切成多個(gè)部分,分配到多個(gè)機(jī)器上,因此更容易出現(xiàn)同步問(wèn)題。
REDgraph 作為一個(gè)在線的圖查詢系統(tǒng),選擇的是邊切分的方案。
2.3 優(yōu)化方案 1.0
· 通用性差,且在 3 跳場(chǎng)景中效果還不夠。
我們之前已經(jīng)實(shí)施了一些優(yōu)化,可以稱之為優(yōu)化方案 1.0。當(dāng)時(shí)主要考慮的是如何快速滿足用戶需求,因此我們的方案包括:首先根據(jù)常用的查詢模式提供一些定制化的算法,這些算法可以跳過(guò)解析、校驗(yàn)、優(yōu)化和執(zhí)行等繁瑣步驟,直接處理請(qǐng)求,從而實(shí)現(xiàn)加速。其次,我們會(huì)對(duì)每個(gè)頂點(diǎn)的扇出操作進(jìn)行優(yōu)化,即每個(gè)頂點(diǎn)在向外擴(kuò)展時(shí),對(duì)其擴(kuò)展數(shù)量進(jìn)行限制,以避免超級(jí)點(diǎn)的影響,降低時(shí)延。此外,我們還完善了算子的下推策略,例如 filter、sample、limit 等,使其盡可能在存儲(chǔ)層完成,以減少網(wǎng)絡(luò)帶寬的消耗。同時(shí),我們還允許讀從節(jié)點(diǎn)、讀寫線程分離、提高垃圾回收頻率等優(yōu)化。
然而,這些優(yōu)化策略有一個(gè)共性,就是每個(gè)點(diǎn)都比較局部化和零散,因此其通用性較低。比如第一個(gè)優(yōu)化,如果用戶需要發(fā)起新的查詢模式,那么此前編寫的算法便無(wú)法滿足其需求,需要另行編寫。第二個(gè)優(yōu)化,如果用戶所需要的是頂點(diǎn)的全部結(jié)果,那此項(xiàng)也不再適用。第三個(gè)優(yōu)化,如果查詢中本身就不存在這些運(yùn)算符,那么自然也無(wú)法進(jìn)行下推操作。諸如此類,通用性較低,因此需要尋找一種更為通用,能夠減少重復(fù)工作的優(yōu)化策略。
2.4 新方案思考
如上圖所示,我們對(duì)一個(gè)耗時(shí)接近一秒的三跳查詢的 profile 分析。我們發(fā)現(xiàn)在每一跳產(chǎn)出的記錄數(shù)量上,第一跳至第二跳擴(kuò)散了 200 多倍,第二跳至第三跳擴(kuò)散了 20 多倍,表現(xiàn)在結(jié)果上,需要計(jì)算的數(shù)據(jù)行數(shù)從 66 條直接躍升至 45 萬(wàn)條,產(chǎn)出增長(zhǎng)速度令人驚訝。此外,我們發(fā)現(xiàn)三跳算子在整個(gè)查詢過(guò)程中占據(jù)了較大的比重,其在查詢層的耗時(shí)更是占據(jù)了整個(gè)查詢的 80% 以上。
那么應(yīng)該如何進(jìn)行優(yōu)化呢?在數(shù)據(jù)庫(kù)性能優(yōu)化方面,有許多可行的方案,主要分為三大類:存儲(chǔ)層的優(yōu)化、查詢計(jì)劃的優(yōu)化以及執(zhí)行引擎的優(yōu)化。
由于耗時(shí)大頭在查詢層,所以我們重點(diǎn)關(guān)注這塊。因?yàn)椴樵冇?jì)劃的優(yōu)化是一個(gè)無(wú)止境的工程,用戶可能會(huì)寫出各種查詢語(yǔ)句,產(chǎn)生各種算子,難以找到一個(gè)通用且可收斂的方案來(lái)覆蓋所有情況。而執(zhí)行引擎則可以有一個(gè)相對(duì)固定的優(yōu)化方案,因此我們優(yōu)先選擇了優(yōu)化執(zhí)行引擎。
圖數(shù)據(jù)庫(kù)的核心就是多跳查詢執(zhí)行框架,而其由于數(shù)據(jù)量大,計(jì)算量大,導(dǎo)致查詢時(shí)間較長(zhǎng),因此我們借鑒了 MPP 數(shù)據(jù)庫(kù)和其他計(jì)算引擎的思想,提出了分布式并行查詢的解決方案。
2.5 原多跳查詢執(zhí)行流程
原有的多跳查詢執(zhí)行流程如上圖所示。假設(shè)我們要查詢 933 頂點(diǎn)的三跳鄰居節(jié)點(diǎn) ID,即檢索到藍(lán)圈中的所有頂點(diǎn)。經(jīng)過(guò)查詢層處理后,將生成右圖所示執(zhí)行計(jì)劃,START 表示計(jì)劃的起點(diǎn),本身并無(wú)實(shí)際操作。GetNeighbor 算子則負(fù)責(zé)實(shí)際查詢頂點(diǎn)的鄰居,例如根據(jù) 933 找到 A 和 B。后續(xù)的 Project、InnerJoin 以及 Project 等操作均為對(duì)先前產(chǎn)生的結(jié)果進(jìn)行數(shù)據(jù)結(jié)構(gòu)的轉(zhuǎn)換、處理及裁剪等操作,以確保整個(gè)計(jì)算流程的順利進(jìn)行。正是后續(xù)的這幾個(gè)算子耗費(fèi)的時(shí)延較高。
2.6 可優(yōu)化點(diǎn)分析
2.6.1 Barrier 等待使時(shí)延增加
從上述物理執(zhí)行中可以看出:查詢節(jié)點(diǎn)必須等所有存儲(chǔ)節(jié)點(diǎn)的響應(yīng)返回后,才會(huì)執(zhí)行后面的算子。這樣即使大多數(shù)存儲(chǔ)節(jié)點(diǎn)很快返回,只要有一個(gè)「慢存儲(chǔ)節(jié)點(diǎn)」存在,整個(gè)查詢都得 block 住。
在圖計(jì)算(AP)場(chǎng)景中,一次計(jì)算往往要經(jīng)過(guò)很多輪迭代(Epoch),并且每輪迭代后都需要進(jìn)行全局指標(biāo)的更新,更新完再繼續(xù)下一輪迭代。在 Epoch 之間插入 Barrier 做同步是有必要的。
但在圖查詢(TP)場(chǎng)景中,通常不需要全局性更新,只是在下發(fā)請(qǐng)求時(shí)對(duì)起點(diǎn) ID 做去重,即使有往往也是在查詢的最后一步,因此沒(méi)有必要 barrier。
此外,圖數(shù)據(jù)庫(kù)負(fù)載往往呈現(xiàn)出“冪律分布”現(xiàn)象,即少數(shù)頂點(diǎn)鄰居邊多、多數(shù)頂點(diǎn)鄰居邊少;REDgraph 本身也是以邊切分的方式存儲(chǔ)數(shù)據(jù),這就使得「慢存儲(chǔ)節(jié)點(diǎn)」很容易出現(xiàn)。加之某個(gè)存儲(chǔ)節(jié)點(diǎn)的網(wǎng)絡(luò)抖動(dòng)或節(jié)點(diǎn)負(fù)載高等因素,可能導(dǎo)致響應(yīng)時(shí)間進(jìn)一步延長(zhǎng),影響查詢效率。
如圖所示,如果查詢層收到一個(gè)響應(yīng)就處理一個(gè)響應(yīng)(類似于 pipeline 的機(jī)制),則能避免無(wú)意義的空等,從整體上加速查詢的執(zhí)行。
2.6.2 查詢層串行執(zhí)行效率低
在整個(gè)查詢計(jì)劃中,只有 GetNeighbor 算子是在多個(gè)存儲(chǔ)節(jié)點(diǎn)上并行執(zhí)行,而其他算子是在查詢節(jié)點(diǎn)上串行執(zhí)行,這里我們想到兩個(gè)問(wèn)題:
- 串行執(zhí)行的效率天然低于并行執(zhí)行。只有在數(shù)據(jù)量太少或者計(jì)算邏輯太簡(jiǎn)單的情況下,上下文切換的開(kāi)銷會(huì)超過(guò)并行的收益。在正常負(fù)載的圖查詢場(chǎng)景中,數(shù)據(jù)量和計(jì)算邏輯都挺可觀;
- 當(dāng)多個(gè)存儲(chǔ)節(jié)點(diǎn)的響應(yīng)數(shù)據(jù)匯聚到查詢節(jié)點(diǎn)時(shí),數(shù)據(jù)量仍然相當(dāng)可觀。如果能在 storaged 節(jié)點(diǎn)上完成這些計(jì)算,將顯著減少查詢節(jié)點(diǎn)需要處理的數(shù)據(jù)量。
我們?cè)跇I(yè)務(wù)的線上集群和性能測(cè)試顯示:GetNeighbors 和 GetVertices 不是所有算子中最耗時(shí)的,反倒是不起眼的 Project 算子往往耗費(fèi)更多時(shí)間,特別是那些緊隨 GetNeighbors 和 GetVertices 之后的 Project 算子,因?yàn)樗粌H需要執(zhí)行數(shù)據(jù)投影,還負(fù)責(zé)將 map 展平。
這表明整個(gè)查詢的主要瓶頸在于計(jì)算量大。而查詢計(jì)劃中大部分都是純計(jì)算型算子,將它們并行化對(duì)于提升查詢效率很有必要。
2.6.3 查詢結(jié)果轉(zhuǎn)發(fā)浪費(fèi) IO
如上文所說(shuō),在圖查詢場(chǎng)景中一般不需要做全局性的更新,查詢節(jié)點(diǎn)收到各存儲(chǔ)節(jié)點(diǎn)的響應(yīng)后,只是簡(jiǎn)單地再次分區(qū)然后下發(fā),所以存儲(chǔ)節(jié)點(diǎn)的結(jié)果轉(zhuǎn)發(fā)到查詢層,再?gòu)牟樵児?jié)點(diǎn)分發(fā)到各存儲(chǔ)節(jié)點(diǎn)很浪費(fèi)。
如果存儲(chǔ)節(jié)點(diǎn)自身具備路由和分發(fā)的能力,那可以讓存儲(chǔ)節(jié)點(diǎn)執(zhí)行完 GetNeighbors 算子后,接著執(zhí)行 Project、InnerJoin 等算子,每當(dāng)遇到下一個(gè) GetNeighbor 算子時(shí),自行組織請(qǐng)求并分發(fā)給其他存儲(chǔ)節(jié)點(diǎn)。其他存儲(chǔ)節(jié)點(diǎn)收到后接著執(zhí)行后面的算子,以此規(guī)則往復(fù),直到最后一步將結(jié)果匯聚到查詢層,統(tǒng)一返回給客戶端。
2.7 改造后的執(zhí)行流程
首先,查詢服務(wù)器(Query Server)將整個(gè)執(zhí)行計(jì)劃以及執(zhí)行計(jì)劃所需的初始數(shù)據(jù)傳輸至存儲(chǔ)服務(wù)器(Store Server),之后 Store Server 自身來(lái)驅(qū)動(dòng)整個(gè)執(zhí)行過(guò)程。以 Store Server 1 為例,當(dāng)它完成首次查詢后,便會(huì)根據(jù)結(jié)果 ID 所在的分區(qū),將結(jié)果轉(zhuǎn)發(fā)至相應(yīng)的 Store Server。各個(gè) Store Server 可以獨(dú)立地繼續(xù)進(jìn)行后續(xù)操作,從而實(shí)現(xiàn)整個(gè)執(zhí)行動(dòng)作的并行化,并且無(wú)同步點(diǎn),也無(wú)需額外轉(zhuǎn)發(fā)。
需要說(shuō)明的是,圖中右側(cè)白色方框比左側(cè)要矮一些,這是因?yàn)閿?shù)據(jù)由上方轉(zhuǎn)到下方時(shí),進(jìn)行了分區(qū)下發(fā),必然比在查詢服務(wù)器接收到的總數(shù)據(jù)量要少。
可以看到,在各部分獨(dú)立驅(qū)動(dòng)后,并未出現(xiàn)等待或額外轉(zhuǎn)發(fā)的情況,Query Server 只需在最后一步收集各個(gè) Store Server 的結(jié)果并聚合去重,然后返回給客戶端。如此一來(lái),整體時(shí)間相較于原始模型得到了顯著縮短。
三、分布式并行查詢的實(shí)現(xiàn)
分布式并行查詢的具體實(shí)現(xiàn),涉及到許多個(gè)關(guān)鍵點(diǎn),接下來(lái)介紹其中一些細(xì)節(jié)。
3.1 如何保證不對(duì) 1-2 跳產(chǎn)生負(fù)優(yōu)化
首先一個(gè)問(wèn)題是,在進(jìn)行改造時(shí)如何確保不會(huì)對(duì)原始的 1-2 跳產(chǎn)生負(fù)優(yōu)化。在企業(yè)內(nèi)部進(jìn)行新的改造和優(yōu)化時(shí),必須謹(jǐn)慎評(píng)估所采取的措施是否會(huì)對(duì)原有方案產(chǎn)生負(fù)優(yōu)化。我們不希望新方案還未能帶來(lái)收益,反而破壞了原有的系統(tǒng)。因此,架構(gòu)總體上與原來(lái)保持一致。在 Store Server 內(nèi)部插入了一層,稱為執(zhí)行層,該層具有網(wǎng)絡(luò)互聯(lián)功能,主要用于分布式查詢的轉(zhuǎn)發(fā)。Query Server 層則基本保持不變。
這樣,當(dāng)接收到用戶的執(zhí)行計(jì)劃后,便可根據(jù)其跳數(shù)選擇不同的處理路徑。若為 1 至 2 跳,則仍沿用原有的流程,因?yàn)樵械牧鞒棠軌驖M足 1-2 跳的業(yè)務(wù)需求,而 3 跳及以上則采用分布式查詢。
3.2 如何與原有執(zhí)行框架兼容
原有代碼中每一個(gè)操作都是用算子方式實(shí)現(xiàn)。為了讓分布式并行查詢的實(shí)現(xiàn)與原有框架兼容,我們把「轉(zhuǎn)發(fā)」也定義為一個(gè)算子,取名為 Forward。這一算子的功能類似于 Spark 中的 Shuffle 算子、或 OceanBase 中的 Exchange 算子,關(guān)鍵在于它能夠確保查詢?cè)诜植际江h(huán)境中順暢執(zhí)行。
我們對(duì)查詢計(jì)劃進(jìn)行了以下關(guān)鍵改寫:
- 在每個(gè)要「切換分區(qū)才能執(zhí)行的」算子前(例如 GetNeighbors、GetVertices 等),我們添加一個(gè) FORWARD 算子。FORWARD 算子負(fù)責(zé)記錄分區(qū)的依據(jù),通常是起點(diǎn) ID。
- 為了將分布式節(jié)點(diǎn)的查詢結(jié)果有效地匯總,我們?cè)诓樵冇?jì)劃的末端添加了 CONVERGE 算子,它指示各節(jié)點(diǎn)將結(jié)果發(fā)送回 DistDriver 節(jié)點(diǎn),即最初接收用戶請(qǐng)求的節(jié)點(diǎn)。
- 隨后,我們引入了 MERGE 算子,它的作用是對(duì)所有從節(jié)點(diǎn)收到的結(jié)果進(jìn)行匯總,并將最終結(jié)果返回給客戶端。
通過(guò)這種方式,當(dāng) REDgraph-Server 準(zhǔn)備執(zhí)行 GetNeighbors、GetVertices 算子時(shí),它會(huì)首先執(zhí)行 FORWARD、CONVERGE算子,將必要的數(shù)據(jù)和查詢計(jì)劃轉(zhuǎn)發(fā)到其他服務(wù)器。這樣,其他服務(wù)器在接收到請(qǐng)求后,就能明確自己的任務(wù)和所需的數(shù)據(jù),從而推動(dòng)查詢計(jì)劃的執(zhí)行。
值得注意的是,F(xiàn)ORWARD 和 CONVERGE算子都有「轉(zhuǎn)發(fā)/發(fā)送」數(shù)據(jù)的含義,不過(guò)它們的側(cè)重點(diǎn)不太一樣:
- FORWARD 強(qiáng)調(diào)的是路由轉(zhuǎn)發(fā),并且要指定轉(zhuǎn)發(fā)的依據(jù),即 partitionKey 字段,不同的數(shù)據(jù)行會(huì)根據(jù)其 partitionKey 字段值的不同轉(zhuǎn)發(fā)到不同的節(jié)點(diǎn)上;
- CONVERGE 強(qiáng)調(diào)的是發(fā)送匯聚,具有單一確定的目標(biāo)節(jié)點(diǎn),即 DistDriver;
因它們只是在做轉(zhuǎn)發(fā)/發(fā)送的工作,我們將這類算子統(tǒng)稱為「路由」算子。
在改造后的查詢計(jì)劃中,從 START 算子開(kāi)始,直到遇到「路由」算子,這多個(gè)算子都可以在某個(gè)節(jié)點(diǎn)本地執(zhí)行的,因此我們將這一系列算子劃分到一個(gè) stage 內(nèi)。整個(gè)查詢計(jì)劃由多個(gè) stage 組成,其中首尾兩個(gè) stage 在 DistDriver 上執(zhí)行,中間的 stage 在 DistWorker 上執(zhí)行。
需要注意的是:stage 是一個(gè)邏輯概念,具體執(zhí)行時(shí),每個(gè) stage 會(huì)依據(jù)分區(qū)和所屬節(jié)點(diǎn)產(chǎn)生多個(gè) task,這些 task 會(huì)分布在多個(gè)節(jié)點(diǎn)上執(zhí)行,每個(gè)節(jié)點(diǎn)僅訪問(wèn)本節(jié)點(diǎn)內(nèi)數(shù)據(jù),無(wú)需跨網(wǎng)絡(luò)拉取數(shù)據(jù)。這種結(jié)構(gòu)化的方法極大地提高了查詢的并行性和效率。
3.3 如何做熱點(diǎn)處理
在原查詢模式中,每一次在發(fā)起 GetNeighbors 算子前,查詢層會(huì)對(duì)起點(diǎn) ID 去重(查詢計(jì)劃中 GetNeighbors 算子的 dedup 為 true),收到存儲(chǔ)節(jié)點(diǎn)的響應(yīng)后,再依靠后續(xù)算子將結(jié)果按需展平,因此存儲(chǔ)節(jié)點(diǎn)不會(huì)產(chǎn)生重復(fù)查詢。以下圖舉例說(shuō)明:
原查詢模式的執(zhí)行流程可簡(jiǎn)單描述為:
- 第一跳從存儲(chǔ)層查到 A->C 和 B->C,返回到查詢層;
- 查詢層會(huì) Project 得到兩個(gè) C,以備后面做 InnerJoin;
- 準(zhǔn)備執(zhí)行第二跳,構(gòu)造起點(diǎn)集合時(shí),由于 dedup 為 true,僅會(huì)保留一個(gè) C;
- 第二跳從存儲(chǔ)層查到 C->D 和 C->E,返回到查詢層;
- 查詢層執(zhí)行 InnerJoin,由于此前有兩個(gè) C,所以 C->D 和 C->E 也各會(huì)變成兩個(gè);
- 查詢層再次 Project 取出 dstId2,得到結(jié)果 D、D、E、E。
從步驟 4 可以看到,存儲(chǔ)層不會(huì)產(chǎn)生重復(fù)查詢。
改造成分布式查詢后,我們只能在每個(gè) stage 內(nèi)去重。但由于缺乏全局 barrier,多個(gè) stage 先后往某個(gè) DistWorker 轉(zhuǎn)發(fā)請(qǐng)求時(shí),多個(gè)請(qǐng)求之間可能有重復(fù)的起點(diǎn),會(huì)在存儲(chǔ)層產(chǎn)生重復(fù)查詢和計(jì)算,導(dǎo)致 CPU 開(kāi)銷增加以及查詢時(shí)延增加。
如果每一跳產(chǎn)生的重復(fù)終點(diǎn) ID(將會(huì)作為下一跳的起點(diǎn) ID)很多,分布式查詢反而會(huì)帶來(lái)劣勢(shì)。為解決這一問(wèn)題,我們引入一套起點(diǎn) ID 去重機(jī)制 —— NeighborCache,具體方案如下:
因?yàn)闆](méi)有全局的 Barrier,無(wú)法在下發(fā)請(qǐng)求之前去重,我們選擇在存儲(chǔ)節(jié)點(diǎn)上提供一個(gè) NeighborCache,其本質(zhì)就是一個(gè) map,可表示為 map<vid +="" edgetype,="" list>。在執(zhí)行 GetNeighbors 算子前,存儲(chǔ)節(jié)點(diǎn)會(huì)首先檢查 NeighborCache,如果找到相應(yīng)的條目,則直接使用這些數(shù)據(jù)填充結(jié)果集;如果沒(méi)有找到,則訪問(wèn)存儲(chǔ)層獲取數(shù)據(jù),并更新 NeighborCache,讀取和更新 Cache 需要用讀寫鎖做好互斥。
另外,NeighborCache 還具有如下特點(diǎn):
- 每當(dāng)有更新 vid + edgeType 的請(qǐng)求時(shí),都會(huì)先 invalidate cache 中對(duì)應(yīng)的條目,以此來(lái)保證緩存與數(shù)據(jù)的一致性;
- 即使沒(méi)有更新操作存在,cache 內(nèi)的每個(gè) kv 條目存活時(shí)間也很短,通常為秒級(jí),超過(guò)時(shí)間就會(huì)被自動(dòng)刪除。為什么這么短呢?
- 充分性:由于在線圖查詢(OLTP)的特性,用戶的多跳查詢通常在幾秒到十幾秒內(nèi)完成。而 NeighborCache 只是為了去重而設(shè)計(jì),來(lái)自于不同 DistWorker 的 GetNeighbors 請(qǐng)求大概率不會(huì)相隔太長(zhǎng)時(shí)間到達(dá),所以 cache 本身也不需要存活太久;
- 必要性:從 map 結(jié)構(gòu)的 key 就會(huì)發(fā)現(xiàn),當(dāng) qps 較高,跳數(shù)多,頂點(diǎn)的鄰居邊多時(shí),此 map 要承載的數(shù)據(jù)量是非常大的,所以也不能讓其存活的時(shí)間太久,否則很容易 OOM;
- 在填充 cache 前會(huì)做內(nèi)存檢查,如果本節(jié)點(diǎn)當(dāng)前的內(nèi)存水位已經(jīng)比較高,則不會(huì)填充,以避免節(jié)點(diǎn) OOM。
通過(guò)這種起點(diǎn) ID 去重機(jī)制,我們能夠有效地減少重復(fù)查詢,提高分布式查詢的效率和性能。
3.4 如何做負(fù)載均衡
第四個(gè)問(wèn)題是怎么做負(fù)載均衡,包括存儲(chǔ)的均衡和計(jì)算的均衡。
首先,存儲(chǔ)的均衡在以邊切分的圖存儲(chǔ)里面其實(shí)是很難的,因?yàn)樗烊坏木褪前秧旤c(diǎn)和其鄰居全部都存在了一起,這是圖數(shù)據(jù)庫(kù)相比其他數(shù)據(jù)庫(kù)的優(yōu)勢(shì),也是其要承擔(dān)的代價(jià)。所以目前沒(méi)有一個(gè)徹底的解決方法,只能在真的碰到此問(wèn)題時(shí)擴(kuò)大集群規(guī)模,讓數(shù)據(jù)的哈希打散能夠更加均勻一些,避免多個(gè)熱點(diǎn)都落在同一個(gè)機(jī)器的情況。而在目前的業(yè)務(wù)場(chǎng)景上來(lái)看,其實(shí)負(fù)載不均衡的現(xiàn)象不算嚴(yán)重,例如風(fēng)控的一個(gè)比較大的集群,其磁盤用量最高和最低的也不超過(guò) 10%,所以問(wèn)題其實(shí)并沒(méi)有想象中的那么嚴(yán)重。
另外一個(gè)優(yōu)化方法是在存儲(chǔ)層及時(shí)清理那些過(guò)期的數(shù)據(jù),清理得快的話也可以減少一些不均衡。
計(jì)算均衡的問(wèn)題。存儲(chǔ)層采用了三副本的策略,若業(yè)務(wù)能夠接受弱一致的讀?。▽?shí)際上大多數(shù)業(yè)務(wù)均能接受),我們可以在請(qǐng)求轉(zhuǎn)發(fā)時(shí),查看三副本中的哪個(gè)節(jié)點(diǎn)負(fù)載較輕,將請(qǐng)求轉(zhuǎn)發(fā)至該節(jié)點(diǎn),以盡量平衡負(fù)載。此外,正如前文所述,熱點(diǎn)結(jié)果緩存也是一種解決方案,只要熱點(diǎn)處理速度足夠快,計(jì)算的不均衡現(xiàn)象便不易顯現(xiàn)。
3.5 如何做流程控制
在分布式查詢架構(gòu)中,由于前面取消全局 Barrier,使得各個(gè) DistWorker 自行驅(qū)動(dòng)查詢的進(jìn)行。這種設(shè)計(jì)提高了靈活性,但也帶來(lái)新的挑戰(zhàn):
如圖所示,各個(gè) DistWorker 上 stage3 的結(jié)果需要匯聚到 DistDriver 后才能向客戶端返回,但是 DistDriver 只在 stage0 的時(shí)候給 Node2 發(fā)送了請(qǐng)求,后面的所有轉(zhuǎn)發(fā)都是由 DistWorker 自行完成的,脫離了 DistDriver 的「掌控」。這樣 DistDriver 就不知道最后有多少個(gè)節(jié)點(diǎn)在執(zhí)行 stage3,也就不知道該等待哪些 DistWorker 給它發(fā)送結(jié)果,以及何時(shí)可以開(kāi)始執(zhí)行 stage4。
我們引入一個(gè)進(jìn)度匯報(bào)機(jī)制:在 DistDriver 上實(shí)現(xiàn)一個(gè) Acker,負(fù)責(zé)接收各個(gè) DistWorker 上報(bào)的 stage 執(zhí)行進(jìn)度信息。每個(gè) stage 向外擴(kuò)散時(shí),向 Acker 發(fā)送一條消息,記錄當(dāng)前完成的 stage 和 即將開(kāi)始的 stage 的節(jié)點(diǎn)數(shù)量。具體而言,就是包含兩個(gè)鍵值對(duì):
- 當(dāng)前的 stage 編號(hào) -> -1;
- 下一個(gè) stage 的編號(hào) -> 執(zhí)行下一個(gè) stage 的節(jié)點(diǎn)的數(shù)量;
比如 Node2 上的 stage-1 擴(kuò)散到 stage-2 時(shí),目標(biāo)節(jié)點(diǎn)有 3 個(gè):Node1、Node3、Node5,于是就發(fā)送 {stage-1: -1,stage-2: 3} 的消息到 DistDriver 上,表示有一個(gè)節(jié)點(diǎn)完成了 stage-1,有 3 個(gè)節(jié)點(diǎn)開(kāi)始了 stage-2。而由于 stage-1 此前由 Node1 登記過(guò) {stage-1: 1},這樣一正一負(fù)就表示所有的 stage-1 都已經(jīng)執(zhí)行完畢。stage-2 和 stage-3 的更新和判定方式類似,當(dāng) DistDriver 發(fā)現(xiàn)所有的前置 stage 數(shù)量都為 0 時(shí),就可以驅(qū)動(dòng) stage-4 。
我們實(shí)際想要的是每個(gè) stage 數(shù)量的正負(fù)抵消能力,而非 {stage-1: -1,stage-2: 3} 的字符串。為了簡(jiǎn)化這一過(guò)程,我們便采用異或運(yùn)算(相同為 0,相異為 1)跟蹤各個(gè) stage 的狀態(tài),舉例說(shuō)明:
- Acker 上有一個(gè)初始的 checksum 值 0000;
- stage-0 在擴(kuò)散到 stage-1 時(shí),生成了一個(gè)隨機(jī)數(shù) 0010(這里為了表達(dá)簡(jiǎn)便,以 4 位二進(jìn)制數(shù)代替),這個(gè) 0010 是 Node2 上的 stage-1 的 Id,然后把這個(gè) 0010 伴隨著 Forward 請(qǐng)求發(fā)到 Node2 上,同時(shí)也發(fā)到 Acker 上,這樣就表示 0010 這個(gè) stage 開(kāi)始了,Acker 把收到的值與本地的 checksum 做異或運(yùn)算,得到 0010,并以此更新本地 checksum;
- stage-1 執(zhí)行完后擴(kuò)散到 stage-2 時(shí),由于有 3 個(gè)目標(biāo)節(jié)點(diǎn),就生成 3 個(gè)不相同的隨機(jī)數(shù) 0101、0001、1010(需要檢查這 3 個(gè)數(shù)異或之后不為 0),分別標(biāo)識(shí) 3 個(gè)目標(biāo)節(jié)點(diǎn)上的 stage-2,然后把這 3 個(gè)數(shù)伴隨著 Forward 請(qǐng)求發(fā)到 Node1、Node3、Node5 上,同時(shí)在本地把自身的 stage Id(0010)和這 3 個(gè)數(shù)一起做異或運(yùn)算,再把運(yùn)算的結(jié)果發(fā)到 Acker,Acker 再次做異或運(yùn)算,也就是 0010 ^ (0010 ^ 0101 ^ 0001 ^ 1010),這樣 0010 就被消除掉了,也就表示 stage-1 執(zhí)行完成了;
- 重復(fù)上述過(guò)程,最后 Acker 上的 checksum 會(huì)變回 0,表示可以驅(qū)動(dòng) stage-4。
注意:盡管在某個(gè)節(jié)點(diǎn)的 stage 擴(kuò)散時(shí)檢查了生成的隨機(jī)數(shù)異或不為 0,但是多個(gè)節(jié)點(diǎn)間生成的隨機(jī)數(shù)異或到一起還是可能為 0 的,比如 Node1 的 stage-2 生成的 3 個(gè)數(shù)異或后為 0001,Node3 的 stage-2 異或后為 0010,Node5 的 stage-2 異或后為 0011,0001 ^ 0010 ^ 0011 = 0。這樣就會(huì)導(dǎo)致 stage-3 還在執(zhí)行中時(shí),DistDriver 就誤認(rèn)為它已經(jīng)執(zhí)行完畢,提前驅(qū)動(dòng) stage-4 的執(zhí)行。
不過(guò)考慮到我們實(shí)際使用的是 int32 整數(shù),出現(xiàn)這種的情況的概率非常低。在未來(lái)的優(yōu)化中在,我們可以考慮給每個(gè) Node 生成一個(gè) 16 位的隨機(jī) Id(由 metad 生成),并保證這些 NodeId 異或結(jié)果不為 0,當(dāng) stage 擴(kuò)散時(shí),將 NodeId 置于隨機(jī)數(shù)的高位,確保分布式查詢的每個(gè)階段都能被準(zhǔn)確跟蹤和協(xié)調(diào)。
另一個(gè)重要的問(wèn)題便是全程鏈路的超時(shí)自檢,例如在 stage2 或 stage3 的某一個(gè)節(jié)點(diǎn)上運(yùn)行時(shí)間過(guò)長(zhǎng),此時(shí)不能讓其余所有節(jié)點(diǎn)一直等待,因?yàn)榭蛻舳艘呀?jīng)超時(shí)了。因此我們?cè)诿總€(gè)算子內(nèi)部的執(zhí)行邏輯中都設(shè)置了一些埋點(diǎn),用以檢查算子的執(zhí)行是否超過(guò)了用戶側(cè)的限制時(shí)間,一旦超過(guò),便立即終止自身的執(zhí)行,從而迅速地自我銷毀,避免資源的無(wú)謂浪費(fèi)。
四、性能測(cè)試
我們?cè)诟脑旃こ掏瓿珊筮M(jìn)行了性能測(cè)試,采用 LDBC 組織提供的 SNB 數(shù)據(jù)集,生成了一個(gè) SF100 級(jí)別的社交網(wǎng)絡(luò)圖譜,規(guī)模達(dá)到 3 億頂點(diǎn),18 億條邊。我們主要考察其一跳、二跳、三跳、四跳等多項(xiàng)查詢性能。
根據(jù)測(cè)試結(jié)果顯示,在一跳和二跳情況下,原生查詢和分布式查詢性能基本相當(dāng),未出現(xiàn)負(fù)優(yōu)化現(xiàn)象。從三跳起,分布式查詢相較于原生查詢能實(shí)現(xiàn) 50% 至 60% 的性能提升。例如,在 Max degree 場(chǎng)景下的分布式查詢已將時(shí)延控制在 50 毫秒以內(nèi)。在帶有 Max degree 或 Limit 值的情況下,時(shí)延均在 200 毫秒以下。盡管數(shù)據(jù)集與實(shí)際業(yè)務(wù)數(shù)據(jù)集存在差異,但它們皆屬于社交網(wǎng)絡(luò)領(lǐng)域,因此仍具有一定的參考價(jià)值。
四跳查詢,無(wú)論是原始查詢還是分布式查詢,其時(shí)延的規(guī)?;旧隙荚诿胫潦嗝氲姆秶鷥?nèi)。因?yàn)樗奶樵兩婕暗臄?shù)據(jù)量實(shí)在過(guò)于龐大,已達(dá)到百萬(wàn)級(jí)別,僅依賴分布式并行查詢難以滿足需求,因此需要采取其他策略。然而,即便如此,我們所提出的改進(jìn)方案相較于原始查詢模式仍能實(shí)現(xiàn) 50% 至 70% 的提升,效果還是很可觀的。
五、總結(jié)與展望
在過(guò)去的較短時(shí)間內(nèi),我們基于 MPP 的理念,對(duì) REDgraph 在分布式并行查詢上進(jìn)行了深入探索和實(shí)踐。本方案能顯著優(yōu)化多跳查詢的性能,并且對(duì)業(yè)務(wù)邏輯完全兼容,沒(méi)有使用限制條件,屬于框架級(jí)的通用優(yōu)化。測(cè)試結(jié)果顯示,時(shí)延降低了 50% 以上,滿足在線業(yè)務(wù)場(chǎng)景的時(shí)延要求,驗(yàn)證方案的有效性。
目前,許多公司的圖數(shù)據(jù)庫(kù)產(chǎn)品在在線場(chǎng)景中仍使用兩跳及以下的查詢。這是因?yàn)槎嗵樵兊臅r(shí)延無(wú)法滿足在線業(yè)務(wù)的要需求,導(dǎo)致失去許多潛在的業(yè)務(wù)價(jià)值,也未能充分發(fā)揮圖數(shù)據(jù)庫(kù)的技術(shù)優(yōu)勢(shì)。隨著小紅書(shū) DAU 的持續(xù)增長(zhǎng),業(yè)務(wù)數(shù)據(jù)規(guī)模朝著萬(wàn)億級(jí)規(guī)模遞增,業(yè)務(wù)上使用替代方案的瓶頸會(huì)逐漸展露。我們計(jì)劃在今年上半年完成開(kāi)發(fā)工作,并在下半年開(kāi)始將這套新架構(gòu)逐步應(yīng)用于相關(guān)業(yè)務(wù)場(chǎng)景。
本方案雖然針對(duì)的是圖數(shù)據(jù)庫(kù),但其探索實(shí)踐對(duì)公司其他數(shù)據(jù)庫(kù)產(chǎn)品同樣具有重要的參考價(jià)值。例如,REDtable 在處理用戶請(qǐng)求時(shí),經(jīng)常需要應(yīng)對(duì)復(fù)雜或計(jì)算量大的查詢,以往會(huì)建議用戶修改代碼來(lái)適應(yīng)這些情況?,F(xiàn)在,我們可以借鑒本方案,為這些「具有重查詢需求」產(chǎn)品打造高性能執(zhí)行框架,以增強(qiáng)自身的數(shù)據(jù)處理能力。
我們將繼續(xù)提升 REDgraph 的多跳查詢能力,并將其和 REDtao 融合,打造成一個(gè)統(tǒng)一的數(shù)據(jù)庫(kù)產(chǎn)品,賦能更多業(yè)務(wù)場(chǎng)景。我們誠(chéng)邀對(duì)技術(shù)有極致追求,志同道合的同學(xué)一起加入團(tuán)隊(duì),共同推動(dòng)圖數(shù)據(jù)技術(shù)的發(fā)展。
六、作者簡(jiǎn)介
- 再興
小紅書(shū)基礎(chǔ)架構(gòu)存儲(chǔ)組工程師,負(fù)責(zé)自研分布式表格存儲(chǔ) REDtable(NewSQL),參與分布式圖數(shù)據(jù)庫(kù) REDgraph 的研發(fā)。 - 敬德
小紅書(shū)基礎(chǔ)架構(gòu)存儲(chǔ)組工程師,負(fù)責(zé)自研圖存儲(chǔ)系統(tǒng) REDtao 和分布式圖數(shù)據(jù)庫(kù) REDgraph。 - 劉備
小紅書(shū)基礎(chǔ)架構(gòu)存儲(chǔ)組負(fù)責(zé)人,負(fù)責(zé) REDkv / Redis / REDtao / REDtable / REDgraph / MySQL 的整體架構(gòu)和技術(shù)演進(jìn)。