大數據文摘投稿作品
知識圖譜數據庫是NoSQL數據庫中增速最快的一個分支,它在大數據和人工智能領域的地位逐漸凸顯。但是目前主流的圖數據庫產品大都屬於海外產品,且售價極其高昂,為了解各大主流圖數據庫的讀寫性能指標,特將國產的新興圖數據庫AbutionGraph(AbutionGDB)與Neo4j,JanusGraph,TigerGraph等占據着市場95%份額的主流圖數據庫做了讀寫性能對比測試。
特別說明:AbutionGDB是唯一面向OLAP(數據分析決策)場景的圖數據倉庫,而其它對比者是面向OLTP(數據增刪改查)的圖數據庫,不過這並不影響讀寫性能的測試。
表1:實時批量寫入事件數性能測試結果
表2:一度關系統計查詢性能測試
測試用數據說明
測試采用的數據來源於互聯網的消費/轉賬記錄模擬數據,每行記錄包含6個字段,分別是:付款方帳號(4bytes長整型)、付款方名字(11bytes字符串)、收款方帳號(4bytes長整型)、收款方名字(11bytes字符串)、交易時間(7bytes日期類型)、交易金額(8bytesBigdicemal雙精度浮點數),數據長度最低是45bytes,因為交易金額的長度是個不確定的值。數據樣例如下:
圖1數據示例
圖中的數據時間戳在測試中進行了調整,進入到系統中的時間戳沒有采用如圖所示的非嚴格的RFC3339格式,而是使用與1970-01-0100:00:00(UTC)時間的差(精確到毫秒)的時間戳表示方式(8bytes的長整型),進入到系統中的金額數據也沒有采用如圖所示的Double標准雙精度浮點數類型表示,而是采用Bigdicemal非基本類型來存儲交易金額,因為交易金額很大時,與歷史記錄聚合很可能發生損失精度的情況,由於系統錯誤導致的財產損失這是絕對不允許的。為便於測試,實際數據均由計算機隨機生成,實體數限制為10w個並按照上述格式和數據庫語法格式寫入各數據庫。
因為每款圖數據庫使用的語言和方式都不一樣,本次主要測評過程以圖譜建模的最終結構為標准,使用的KnowledgeGraph建模如下:
統計結果說明
對於任何一個測試,每個操作過程重復運行5次,且最終的統計結果是5次測試結果的算數平均值。
每次測試進行之前均重啟數據庫服務,避免緩存對於之前執行獲得的結果產生影響。
本文中所說的延遲(latency)是指全部完成該操作所耗費的時間,例如針對多條記錄的查詢操作,其時間延遲是指完全將結果獲取到客戶端后,相對於請求發出時間之間的間隔。吞吐量(throughput)是單位時間內完整完成該操作的數量。
AbutionGDB與其他數據庫單節點對比測試
測試環境及步驟說明
所有數據庫的對比測試在同一台8H16G的阿里雲服務器上進行,該服務器的詳細配置如下:架構: x86_64CPU: 8CPU運行模式: 32-bit, 64-bitSize: 16 GB每個核的線程數: 2每個座的核數: 4座: 1廠商ID: GenuineIntelCPU系列: 6型號: 85型號名稱: Intel(R)Xeon(R)Platinum8269CYCPU@2.50GHzCPUMHz: 2499.998BogoMIPS: 4999.99超管理器廠商: KVM虛擬化類型: 完全LSB Version: core-4.1-amd64:core-4.1-noarchDescription: CentOS Linux release 8.2.2004 (Core) 本測試將AbutionGDB與Neo4j,JanusGraph,TigerGraph等圖數據庫進行了對比,所測試的Neo4j版本為3.5.24;JanusGraph的版本為0.5.2(使用Hbase后端存儲);TigerGraph的版本為3.0.0,而AbutionGDB的測試版本為1.3.0,截止至2021年,皆為jdk-1.8支持下的最新版本。
在測試時,AbutionGDB、Janasgraph、Neo4j均使用JavaAPI並結合各自的查詢語言進行數據操作,由於TigerGraph只提供了HTTP接口,在測試中我們采用Java語言的Apache HTTP lib庫來編寫測試程序。
AbutionGDB提供更優異的異步接口,支持使用Flink作為大規模實時數據源寫入數據,但Janasgraph、Neo4j、TigerGraph均不支持。只有AbutionGDB和TigerGraph可以使用Kafka作為實時數據源寫入數據,為了測試一致性及公平性,僅使用各自推薦的同步接口方式進行讀寫測試。在本次測試中,AbutionGDB、Janasgraph及TigerGraph都采用默認配置,Neo4j則只修改dbms.memory.heap.initial_size和dbms.memory.heap.max_size,由默認的512m改到5g,以最大程度發揮Neo4j性能。
為避免網絡延遲的影響,應用與數據庫均在同一台服務器上運行。為更進一步控制變量,本測試確保在對每個數據庫進行測試時只有該數據庫運行,因此數據庫所能占用的CPU資源最高可達100%。
本測試測試了各個數據庫在不同客戶端連接數同時寫入和讀取不同批量數據的表現,且每種情況均進行至少5次測試,最終結果為5次測試結果的平均值。同時為了避免測試與測試之間的干擾,每次測試結束后,都會刪除數據,重啟數據庫服務並且等待5分鍾左右。
寫入性能對比
數據庫的一個寫入請求可以包含一條或多條記錄,性能結果取每個客戶端每秒內同時寫入數據量的總和,並取多次平均值。總而言之,一次請求里包含的記錄條數越多,寫入性能就會相應提升。
同時,一個數據庫可以支持多個客戶端鏈接,鏈接數增加,其系統總的插入通吐量會相應增加。因此測試中,對於每一個數據庫,都會測試一個客戶端和多個客戶端連接的情況。
在AbutionGDB中,可以定義任意多維度的指標列,其中動態聚合的存儲模型是其特有的,其中預計算模型可以大大提高查詢性能,而靜態歷史數據的存儲模型是與Janasgraph、Neo4j、TigerGraph一致的,為了凸顯寫入事件的速率和查詢性能的公平性,我們定義每一條完整的原始交易數據入庫完成才作為寫入一條數據,即一條事件數, 而不是以每個實體或者每條關系作為一條數據。
AbutionGDB批量實時寫入結果
此測試中,基於AbutionGDB強大的吞吐量,我們專門把寫入事件數的區間拉大,按照每次請求包含1,100,1000,10000,100000,200000條事件記錄各進行了測試,同時也測試了不同客戶端連接數的數據。具體結果如下:
批次插入事件數 | 1client | 2client | 3client | 4client | 5client | 6client | 7client |
1 | 1339 | 2628 | 3904 | 5177 | 6516 | 7843 | 9243 |
100 | 24599 | 50099 | 71167 | 87467 | 99567 | 112667 | 136267 |
1000 | 44343 | 84931 | 119931 | 145931 | 166907 | 195273 | 235861 |
10000 | 58878 | 95295 | 130416 | 160418 | 190975 | 199025 | 236070 |
100000 | 99040 | 129773 | 172025 | 196447 | 232315 | 267567 | 251729 |
200000 | 187402 | 229706 | 207413 | 233558 | 263547 | 264271 | 267015 |
因本測試將3條圖數據統一歸為1條事件數統計(每條事件包含2個實體1條關系),按照數據庫寫入性能標准,以上測試結果應該均乘以3即為真實寫入速率。具體結果如下:
批次插入事件數 | 1client | 2client | 3client | 4client | 5client | 6client | 7client |
1 | 4017 | 7884 | 11712 | 16713 | 19548 | 23529 | 36729 |
100 | 73797 | 150297 | 213501 | 262401 | 298728 | 338001 | 408801 |
1000 | 133029 | 254793 | 359793 | 437793 | 500721 | 585819 | 707583 |
10000 | 176634 | 285855 | 391248 | 481254 | 572925 | 597075 | 708210 |
100000 | 297120 | 389319 | 516075 | 589341 | 696945 | 802701 | 755187 |
200000 | 562206 | 689118 | 622239 | 700674 | 790641 | 792813 | 801045 |
上述結果清楚表明,AbutionGDB的寫入速度隨客戶端連接數的增加近乎線性增加,而且隨單請求中記錄數目的增加而增加。在七個客戶端下,一個請求,寫入1條數據時,AbutionGDB就有超過10,000記錄/秒(30,000條圖數據/秒)的寫入速度。在一個寫入請求200,000條記錄時,在7個客戶端同時寫入時的情況下,AbutionGDB有高達267,015記錄/秒(801,045條圖數據/秒)的寫入速度。同時,從上圖中可以看出,這個速度並非AbutionGDB寫入速度的上限,該速度還會隨客戶端數目的增加而增加。
Neo4j批量實時寫入結果
在測試中,我們嘗試了兩種方式來更全面的測試Neo4j寫入性能,第一種是傳統的JDBC方式,通過DriverManager接收SQL,同時禁用了AutoCommit,采用批量手動提交commit的方式,Neo4j本身沒有此項功能支持,需要額外下載neo4j-jdbc-driver和neo4j-jdbc-bolt擴展包支持;第二種是使用的是官方推薦的bolt Driver連接方式,此方是沒有commit方法,Neo4j自動提交數據變更,所以測試只能通過客戶端的連接數觀察寫入性能,為此,我們做了5組測試來分別觀察客戶端的連接數對於寫入性能的影響。由於Neo4j不支持BigDecimal數據類型,因而交易時間將采用Double來存儲。
通過測試發現,兩種方式對於寫入速率沒有實質性差距,Neo4j的批量提交寫入並不能有效提升其寫入效率,因此僅僅記錄了Neo4j連續寫入的多個客戶端同時寫入來達到批量寫入數據的效果。結果如下:
事件數 | 1client | 2client | 3client | 4client | 5client |
test1 | 72 | 68 | 63 | 56 | 59 |
test2 | 70 | 70 | 64 | 54 | 61 |
test3 | 73 | 68 | 62 | 55 | 56 |
test4 | 73 | 68 | 62 | 55 | 53 |
test5 | 72 | 68 | 66 | 56 | 57 |
從上述結果表可以看出,Neo4j僅適用於單客戶端單條記錄的寫入,多組對照實驗結果幾乎相同,證明其寫入性能並不會由於客戶端鏈接數量的增加或單請求中記錄條數的增加而顯著改變。
JanusGraph批量實時寫入結果
在測試中發現,JanusGraph的寫入吞吐量與客戶端連接數沒有太大關系,而與批處理時單請求中的記錄數相關。批請求中記錄數目越大,其吞吐量也越大,但是當數據量達到1w左右的時候就出現了嚴重的寫入抖動,速率在3000-1w/s間波動。由於客戶端連接數對JanusGraph的吞吐量影響較小,本測試只給出JanusGraph在單客戶端和雙客戶端連接情況下的測試結果,且分別對每個客戶端按照每次請求包含1,100,300,500,1000,2000,10000,20000條事件記錄作出測試。測試結果計算為每秒寫入的數據總量,多秒完成的寫入計算結果為平均值。測試結果如下:
事件數 | 1 | 100 | 300 | 500 | 1000 | 2000 | 10000 | 20000 |
1client | 316 | 3700 | 5400 | 8300 | 8200 | 8000 | 6666 | 6999 |
2client | 4140 | 5070 | 7220 | 5450 | 5950 | 5800 | 7250 | 6333 |
上述結果表明,JanusGraph的寫入速度隨單請求中的事件數目的增加而增加,但最終增長速度趨緩,並在每次提交事件數在1000條左右到達寫入最佳性能,同時也到達了JanusGraph的寫入瓶頸,再隨着每批次寫入數據的不斷增加,寫入性能不增反降,且最終寫入速度維持在7000事件/秒上下。
TigerGraph批量實時寫入結果
本次我們測試的是TigerGraph,由於TigerGraph官方只提供基於HTTP的REST使用接口,此方式寫入較慢,無法批量寫入,所以我們使用開源的jdbc方式,且修補了一些版本匹配問題,此方式其實是HTTP接口的一種封裝。Jdbc提供了3種寫入方法,提交job的方式是離線導入,第2種方法PreparedStatement的寫入語法對Double數據類型參數支持不友好,最終我們選擇使用Statement方法寫入,並以默認的手游轉讓地圖覆蓋歷史數據的形式寫入新數據,對不同對照組的Batch進行批量提交寫入,Statement的方式可以以批量提交的方式進行寫入,與AbutionGDB,Neo4j,JanusGraph測試方式一致。
在測試過程中發現,TigerGraph的寫入性能與客戶端連接數沒有太大關系,與批處理時單請求中的記錄數也沒有太大關系,反而會略微拉低其它客戶端的寫入能力,所以本測試只記錄了1-5個客戶端的同時寫性能,測試結果如下:
事件數 | 1client | 2client | 3client | 4client | 5client |
1 | 143 | 267 | 338 | 469 | 450 |
100 | 250 | 400 | 441 | 580 | 625 |
1000 | 350 | 544 | 588 | 780 | 971 |
2000 | 351 | 510 | 702 | 888 | 976 |
10000 | 370 | 606 | 714 | 848 | 983 |
上述數據表明,基於HTTP接口的數據寫入方式並沒有很高效,隨着客戶端連接數和實時寫入數據量的增加,寫入性能成弱線性增長。
鑒於以上TigerGraph的測試結果不是很高效,我們專門對TigerGraph測試了離線寫入能力,即load job數據寫入方式,這是官方文檔推薦的做法。為了記錄離線寫入性能,我們使用與AbutionGDB,Neo4j,JanusGraph相同的數據生成程序隨機生成一批數據到文件,並將每一條事件記錄拆分為節點數據和關系數據兩個文件分別等待寫入,然后預先分別對每批次寫入數據的5個對照組預先創建LOADING JOB(不計入寫入耗時中),最后在gsql腳本中執行批量導入JOB並記錄耗時,最終統計得出每秒寫入性能。經測試一兩條、幾百條等少批量數據導入對性能表現不明顯,為了節省測試時間,避免生成離線數據的繁瑣過程,我們將從100條每批寫入開始記錄結果。
在測試過程中發現,TigerGraph的寫入性能與客戶端連接數沒有關系,反而會均分掉其它客戶端的寫入能力,所以本測試只對TigerGraph在1和2客戶端連接的情況下的性能進行了測試和分析,測試結果如下:
事件數 | 100 | 1000 | 10000 | 20000 | 60000 | 100000 | 200000 | 500000 |
1client | 17 | 167 | 1751 | 3076 | 10000 | 14285 | 28571 | 76923 |
2client | 10 | 91 | 952 | 2371 | 6315 | 9524 | 21052 | 47619 |
上述結果表明,TigerGraph的離線寫入速度隨單請求中記錄數目的增加而增加,入庫速率與批次入庫數量成正比增長,數據寫入及與節點個數成弱線性增長,也與實時寫入測試的結果增長趨勢成線性關系。
注意:本測試不與AbutionGDB,Neo4j,JanusGraph,TigerGraph已測試過的結果進行比較,因為實時寫入涉及的數據庫cache和flush的性能是性能測試和穩定性測試的重要指標之一,與離線數據測試機理不同,TigerGraph離線導入使用MapReduce並行寫入,這與AbutionGDB和JanusGraph中的離線導入方式基本相同,故此不再展開分別測試。
TigerGraph實時寫二次測試
由於以上TigerGraph的測試結果與官方報道的性能差距太大,所以我們決定增加測試Kafka的寫入接口,為了盡量保證公平性,我們還是使用與AbutionGDB,Neo4j,JanusGraph相同的數據生成程序隨機生成一批數據到Kafka生產者,並在TigerGraph中實時接收批次事件數據。每輪測試我們都將重啟TigerGraph以清除緩存影響,Kafka程序無法准確記錄寫入速率,為了反映寫入速率,我們使用TigerGraph本身的計時器,重啟時同時也清空了上一輪的計時重新計算寫入,多輪對照測試結果取均值,結果幾乎無偏差。
在測試過程中發現,Kafka接口無法啟動多個客戶端在同一台服務器,也必須是配置使用所有的Kafka分區才能啟動寫入,即默認了接收所有傳來的數據,所以TigerGraph單節點寫入與多客戶端沒有太大關系,因為已經是使用最大化資源了,所以我們只對單一客戶端測試,這與AbutionGDB,Neo4j,JanusGraph測試使用的資源幾乎是一致的。測試TigerGraph數據寫入結果截圖如下:
結果中,LOADED MESSAGES表示每批次的實時寫入事件記錄數(包含兩個實體和一條關系),DURATION表示這批次數據入庫的時間。
為了進一步去除網絡數據傳輸等因素影響,我們對每批次寫入耗時統一減去3-4秒的數據接入響應,包括:Kafka生產者被接入消費者的時間,一般為1秒左右;以及數據實時隨機生成耗時,一般為1秒左右。最終得到超低誤差的實時寫入性能如下:
每批插入數: | 1w | 2w | 10w | 20w | 100w | 200w | 500w |
入庫事件/秒: | 4000 | 8000 | 13333 | 25000 | 50000 | 60606 | 80645 |
上述結果表明,TigerGraph對於超大規模數據實時寫性能表現較好,同時也反映了想要獲取更好的性能需要更多的服務器資源。測試中TigerGraph的Kafka實時寫入和離線寫入性能最終趨向一致,入庫速率與批次入庫數量成正比增長。TigerGraph的寫入速度與單請求中記錄數目具有極大關系,且隨着單請求中記錄數目的增加而增加,並始終保持勻速正增長,但增速緩慢。從圖中可以看出,TigerGraph在本測試實時每批次寫入5,000,000條事件記錄和離線每批次寫入500,000條事件記錄時達到最高寫入性能,寫入速度大約為80,000事件記錄/秒。
各數據庫最佳性能對比
基於以上的測試數據,將各數據庫測試出來的最佳速度進行對比,結果如下:
事件數 | 1client | 2client | 3client | 4client | 5client | 6client | 7client |
AbutionGDB | 187402 | 229706 | 207413 | 233558 | 263547 | 264271 | 267015 |
Neo4j | 72 | 68 | 63 | 56 | 59 | 55 | 53 |
JanusGraph | 8300 | 7250 | 7220 | 5450 | 5950 | 5800 | 5700 |
TigerGraph | 80645 | 80645 | 80645 | 80645 | 80645 | 80645 | 80645 |
注意:因為TigerGraph多客戶端實時寫性能很差,且變化很小,所以這里使用Kafka的實時寫加入比較,但又因Kafka接入無多客戶端,所以將TigerGraph每批次寫入的最佳性能並入比較,相較於AbutionGDB,Neo4j,JanusGraph有一定優勢為便於對比,在這里適當不計。 從圖中可以看出,AbutionGDB是4個圖數據庫中唯一一個寫入速度隨客戶端一致線性增加的數據庫,且其寫入速度遠遠高於其他三個數據庫,也是唯一一個突破十萬事件記錄/秒寫入速度的圖數據庫。而TigerGraph、JanusGraph和Neo4j在測試中都展示出了瓶頸。JanusGraph雖然在客戶端較少的情況下就有接近1w的事件(約2.5w實體和關系)寫入速度,但是其速度無法因客戶端增加而線性增加,從圖可知JanusGraph的瓶頸在1w事件記錄/秒。而TigerGraph的實時寫不會因為客戶端的增加而顯著增加寫入性能,始終維持在一個低水平狀態,但在Kafka接口中達到了較好的寫入性能,不過只對於大規模量級數據表現較好。對於Neo4j,其寫入性能不會因為客戶端的增加而提高,反而略微下降,沒有很好的並行性。
綜上所述,AbutionGDB在多客戶端連接同步寫入的速度遠遠高於同等條件下的TigerGraph、JanusGraph和Neo4j等市場主流圖數據庫。AbutionGDB在7個客戶端連接同時寫入的情況下可達到~270,000事件記錄/秒的寫入速度(約810,000條實體與關系),是已測得JanusGraph的最大寫入速度的33倍,是TigerGraph普通接口最大寫入速度的222倍,是Neo4j最大寫入速度的3709倍。
讀取性能對比
本測試做了簡單的查詢測試,就是將插入的數據全部讀出並做一度關系計算與過濾出鄰居數大於5的人。因為各大數據庫的性能差異很大,為節省測試時間,我們使用一個客戶端查詢不同的記錄條數並進行計算。測試結果如下:
查詢響應時間 | AbutionGDB | TigerGraph | JanusGraph | Neo4j |
10w | 220 | 210 | 21040(21s) | 12570(12s) |
20w | 212 | 247 | 40780(40s) | 17920(17s) |
50w | 225 | 359 | 106s | 137s |
100w | 306 | 377 | 380s | 441s |
1000w | 679 | 855 | 無法完成 | 無法完成 |
為了方便查看,我們對每個查詢耗時轉換為統計查詢性能:
查詢響應時間 | AbutionGDB | TigerGraph | JanusGraph | Neo4j |
10w | 455 | 476 | 5 | 8 |
20w | 943 | 810 | 5 | 11 |
50w | 2222 | 1392 | 5 | 4 |
100w | 3268 | 2653 | 3 | 2 |
1000w | 14727 | 11696 | 1 | 1 |
從圖表中可以看出,AbutionGDB的單機最大OLAP讀取速度在15,000記錄/毫秒左右,TigerGraph的最大讀取速度在12,000記錄/毫秒左右,性能幾乎相同。在AbutionGDB中,實現原理是基於特有的預計算模型,數據在寫入的時候就已經自動完成了部分規則統計,查詢時僅需少量后計算資源即可得到結果,這與實時數據倉庫的特性是類似的。
在TigerGraph中,因其高級查詢語句中支持累加器操作,所以我們針對測試場景定制了一個查詢函數,用於累計每個實體的一度關系,它將並行的將函數預先運行到每個實體中,安裝函數的步驟會花費幾秒鍾的時間,以后直接運行查詢就會很快,這有點類似AbutionGDB的預計算模型機制。JanusGraph因為不支持自定義id,查詢起來需要先匹配節點屬性做聚合,再做一度關系計算,每次計算量都會比AbutionGDB和TigerGraph大至少一倍,所以計算速度很慢。
而Neo4j與JanusGraph一樣,導入數據時都會自增生成一個ID來存儲節點,即使相同的事件數據也會重新得到一個新的存儲id,想要加速查詢只能對屬性添加索引支持,所以聚合類查詢都很慢,但因為是原生圖存儲,數據量少的情況下會比JanusGraph略快一些。所以從測試結果來看,AbutionGDB的查詢吞吐量遠高於JanusGraph和Neo4j。
綜上可以看到,AbutionGDB寫入性能具有良好的橫向擴展(scale-out)的能力,對於多節點構成的集群也能提供優秀的擴展支持,而讀取性能也具有千萬級/秒的速度,對於大規模的大多數數據統計查詢場景都能在1秒內返回結果。