Bulk Loading
用于高效導(dǎo)入大量的圖數(shù)據(jù)到j(luò)anusgraph,有一些配置參數(shù)和工具用于bulk loading。而小量數(shù)據(jù)通過獨(dú)立事務(wù),屬于傳統(tǒng)的transactional loading。
舉例一下場(chǎng)景用到 bulk loading:
- 引入janusgraph到一個(gè)已有環(huán)境,存在已有數(shù)據(jù)需要融合或副本到新的janusgraph cluster。
- 使用janusgraph作為ETL過程的end point。
- 增加已存在或外部的圖數(shù)據(jù)集(如RDF datasets)到一個(gè)運(yùn)行中的janusgraph cluster。
- 通過graph分析job的結(jié)果數(shù)據(jù),更新janusgraph的圖中。
下面關(guān)于bulk loading高效配置參數(shù)和工具,請(qǐng)?jiān)谏白屑?xì)留意每個(gè)參數(shù)的約束和前提條件,避免數(shù)據(jù)丟失或數(shù)據(jù)不正確。
除了下面提到的特定優(yōu)化,也需考慮提高storage后端和index后端的寫入性能。
Configuration Options
Batch Loading
激活 storage.batch-loading 配置項(xiàng)將在大多數(shù)情況下最大化提高bulk loading效果。激活的同時(shí)意味著禁用了jannusgraph內(nèi)部多處的數(shù)據(jù)一致性校驗(yàn)。同時(shí)最重要的是,也禁用了locking。反過來(lái)看,janusgraph會(huì)假設(shè)load的數(shù)據(jù)已經(jīng)是保證一致性了,因而禁用janusgraph自檢會(huì)提高性能。
在許多bulk loading場(chǎng)景,先確保數(shù)據(jù)一致性再loading數(shù)據(jù),會(huì)比loading數(shù)據(jù)到db同時(shí)確保數(shù)據(jù)一致性好很多。因此storage.batch-loading配置項(xiàng)會(huì)在這時(shí)有作用。
例如,考慮這種使用場(chǎng)景,bulk loading用戶信息到j(luò)anusgraph,假設(shè)username字段是唯一索引定義,必須保證graph中的唯一性。當(dāng)從另一個(gè)數(shù)據(jù)庫(kù)中導(dǎo)入用戶信息,username已經(jīng)由之前db有了唯一性保證。如果之前的數(shù)據(jù)沒有保證唯一性,也可以用sortby username并去重來(lái)較簡(jiǎn)單的實(shí)現(xiàn),如hadoop做filter。這之后就可以激活storage.batch-loading,janusgraph不再檢查username是否重復(fù),來(lái)大大減少bulk loading的耗時(shí)。
重要:激活storage.batch-loading需要用戶確保load的自身數(shù)據(jù)一致性,且與graph中已有數(shù)據(jù)不重復(fù)。特別是,當(dāng)激活storage.batch-loading時(shí),并發(fā)創(chuàng)建type可能帶來(lái)數(shù)據(jù)完整性問題。因此,強(qiáng)烈建議在設(shè)置storage.batch-loading時(shí)禁用自動(dòng)type創(chuàng)建。
Optimizing ID Allocation
ID Block Size
每個(gè)新增vertex或edge會(huì)分配一個(gè)唯一的id。janusgraph的id pool manager指定id blocks給janusgraph實(shí)例。id block的指定是昂貴的,因?yàn)樾枰WC全局唯一可分配blocks。增加ids.block-size可以減少指定的次數(shù),但也潛在留下更多id未分配而浪費(fèi)的可能。
對(duì)事務(wù)的數(shù)據(jù)寫入,默認(rèn)的block sizes是有理由的,但是在bulk loading時(shí),頂點(diǎn)和邊增加的更加頻繁和快速,因此會(huì)推薦考慮將block size增大10倍,或者取決于每個(gè)機(jī)器需要增加的頂點(diǎn)數(shù)量。
關(guān)鍵規(guī)則:設(shè)置ids.block-size為預(yù)期下每個(gè)janusgraph 實(shí)例每小時(shí)增加的頂點(diǎn)數(shù)量。
重要:所有janusgraph實(shí)例必須被配置為同樣的ids.block-size,確保id分配。因此,修改這個(gè)值時(shí)請(qǐng)確保所有的janusgraph實(shí)例已經(jīng)關(guān)閉。
ID Acquisition Process
當(dāng)很多janusgraph實(shí)例并行的很快的分配id blocks, 實(shí)例間將不可避免出現(xiàn)分配沖突,且減慢id分配的過程。此外,當(dāng)janusgraph認(rèn)為失敗并拋出異常時(shí),bulk loads進(jìn)一步堆積過來(lái)的寫load會(huì)進(jìn)一步減慢id分配的過程。所以有三個(gè)配置項(xiàng)可以優(yōu)化來(lái)避免發(fā)生這種情況。
- ids.authority.wait-time 配置id pool manager等待1個(gè)id block應(yīng)用程序的時(shí)間(單位毫秒),等待storage后端確認(rèn)。這個(gè)時(shí)間越短,在一個(gè)擁擠的storage集群中,一個(gè)應(yīng)用程序越容易fail。(default=300ms)
關(guān)鍵規(guī)則:設(shè)置這個(gè)值,參考storage后端在load壓力下,讀寫時(shí)間sum和的95分位值。
重要:這個(gè)值應(yīng)該相同在所有janusgraph實(shí)例。
- ids.renew-timeout 配置id pool manager請(qǐng)求指派1個(gè)新的id block等待的時(shí)間(單位毫秒),等待超過則failing。
關(guān)鍵規(guī)則:設(shè)置這個(gè)值應(yīng)該合理的大一些,不要特別大而在不可恢復(fù)的錯(cuò)誤中等待太久。增大這個(gè)值的缺點(diǎn)是janusgraph將在storage后端不可用時(shí),嘗試很久。
Optimizing Writes and Reads
Buffer Size
janusgraph會(huì)緩存寫操作,并分成較少的批次執(zhí)行他們,來(lái)減少提交到storage后端的總請(qǐng)求數(shù)量。每批次的寫操作數(shù)量由參數(shù)storage.buffer-size控制。當(dāng)在較短時(shí)間內(nèi)執(zhí)行大量的寫操作,有可能會(huì)讓storage后端被寫操作的請(qǐng)求超載。這時(shí),增加storage.buffer-size能通過提高每次請(qǐng)求包含的寫操作,降低總請(qǐng)求次數(shù),來(lái)避免錯(cuò)誤。
然后,提高緩存size會(huì)增加寫操作的延時(shí),和每次寫操作出錯(cuò)的可能。因此,在事務(wù)load情況不建議增大這個(gè)值,且在bulk loading應(yīng)謹(jǐn)慎增大。
Read and Write Robustness
在bulk loading中,集群的load在讀寫操作會(huì)有更大fail的可能性(特別是如果buffer size如上增大時(shí))。
storage.read-attempts和storage.write-attempts參數(shù)增加重試次數(shù)來(lái)提高操作做健壯性,配置janusgraph會(huì)嘗試多少次到storage后端來(lái)執(zhí)行1次讀寫操作,直到放棄。如果是大量load,建議增大重試次數(shù)。
storage.attempt-wait規(guī)定janusgraph重試間等待的時(shí)間(單位毫秒)。更大的等待時(shí)長(zhǎng)可以確保重試操作不會(huì)進(jìn)一步增加后端的負(fù)載。
Strategies
Parallelizing the Load
通過多個(gè)機(jī)器并行bulk loading,如果janusgraph的storage后端集群足夠強(qiáng)大來(lái)服務(wù)大量并發(fā)請(qǐng)求,load時(shí)間會(huì)大幅度減少。如MapReduce或Spark來(lái)執(zhí)行bulk loading。 JanusGraph with TinkerPop’s Hadoop-Gremlin
如果不使用hadoop來(lái)并行bulk loading,這里也有一些高級(jí)向?qū)в糜诟咝Р⑿刑幚恚?/p>
- 一些情況,graph數(shù)據(jù)能被分解到多個(gè)分開的子圖。這些子圖通過多個(gè)機(jī)器并行獨(dú)立的load。(舉例:用BatchGraph)
- 如果graph不能被分解,常??梢酝ㄟ^下面幾個(gè)步驟load來(lái)收益,最后2步可以在多機(jī)器并行處理:
- 確保vertex和edge去重了,一致性保證;
- 設(shè)置batch-loading=true,還有上面提到的其他參數(shù)也可盡量?jī)?yōu)化。
- 增加所有頂點(diǎn)和其屬性到graph(不包括edge)。維護(hù)所有vertex id到一個(gè)支持分布式讀寫的map里,map里記錄對(duì)象原主鍵id和vertex id(64位long id)之間的映射關(guān)系。
- 增加所有edge,使用map來(lái)查找vertex id,通過id取回vertex建立edge。
Q&A
如何在batch loading中避免如下錯(cuò)誤:
java.io.IOException: ID renewal thread on partition [X] did not complete in time.