溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

怎樣分析圖數(shù)據(jù)庫(kù)

發(fā)布時(shí)間:2021-12-02 14:16:01 來(lái)源:億速云 閱讀:181 作者:柒染 欄目:數(shù)據(jù)庫(kù)

怎樣分析圖數(shù)據(jù)庫(kù),相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。

下面主要討論圖數(shù)據(jù)庫(kù)背后的設(shè)計(jì)思路、原理還有一些適用的場(chǎng)景,以及在生產(chǎn)環(huán)境中使用圖數(shù)據(jù)庫(kù)的具體案例。

從社交網(wǎng)絡(luò)談起

下面這張圖是一個(gè)社交網(wǎng)絡(luò)場(chǎng)景,每個(gè)用戶(hù)可以發(fā)微博、分享微博或評(píng)論他人的微博。這些都是最基本的增刪改查,也是大多數(shù)研發(fā)人員對(duì)數(shù)據(jù)庫(kù)做的常見(jiàn)操作。而在研發(fā)人員的日常工作中除了要把用戶(hù)的基本信息錄入數(shù)據(jù)庫(kù)外,還需找到與該用戶(hù)相關(guān)聯(lián)的信息,方便去對(duì)單個(gè)的用戶(hù)進(jìn)行下一步的分析,比如說(shuō):我們發(fā)現(xiàn)張三的賬戶(hù)里有很多關(guān)于 AI 和音樂(lè)的內(nèi)容,那么我們可以據(jù)此推測(cè)出他可能是一名程序員,從而推送他可能感興趣的內(nèi)容。

怎樣分析圖數(shù)據(jù)庫(kù)

這些數(shù)據(jù)分析每時(shí)每刻都會(huì)發(fā)生,但有時(shí)候,一個(gè)簡(jiǎn)單的數(shù)據(jù)工作流在實(shí)現(xiàn)的時(shí)候可能會(huì)變得相當(dāng)復(fù)雜,此外數(shù)據(jù)庫(kù)性能也會(huì)隨著數(shù)據(jù)量的增加而銳減,比如說(shuō)獲取某管理者下屬三級(jí)匯報(bào)關(guān)系的員工,這種統(tǒng)計(jì)查詢(xún)?cè)诂F(xiàn)在的數(shù)據(jù)分析中是一種常見(jiàn)的操作,而這種操作往往會(huì)因?yàn)閿?shù)據(jù)庫(kù)選型導(dǎo)致性能產(chǎn)生巨大差異。

傳統(tǒng)數(shù)據(jù)庫(kù)的解決思路

傳統(tǒng)數(shù)據(jù)庫(kù)的概念模型及查詢(xún)的代碼

傳統(tǒng)解決上述問(wèn)題最簡(jiǎn)單的方法就是建立一個(gè)關(guān)系模型,我們可以把每個(gè)員工的信息錄入表中,存在諸如 MySQL 之類(lèi)的關(guān)系數(shù)據(jù)庫(kù),下圖是最基本的關(guān)系模型:

怎樣分析圖數(shù)據(jù)庫(kù)

但是基于上述的關(guān)系模型,要實(shí)現(xiàn)我們的需求,就不可避免地涉及到很多關(guān)系數(shù)據(jù)庫(kù) JOIN 操作,同時(shí)實(shí)現(xiàn)出來(lái)的查詢(xún)語(yǔ)句也會(huì)變得相當(dāng)長(zhǎng)(有時(shí)達(dá)到上百行):

(SELECT T.directReportees AS directReportees, sum(T.count) AS count
FROM (
SELECT manager.pid AS directReportees, 0 AS count
    FROM person_reportee manager
    WHERE manager.pid = (SELECT id FROM person WHERE name = "fName lName")
UNION
    SELECT manager.pid AS directReportees, count(manager.directly_manages) AS count
FROM person_reportee manager
WHERE manager.pid = (SELECT id FROM person WHERE name = "fName lName")
GROUP BY directReportees
UNION
SELECT manager.pid AS directReportees, count(reportee.directly_manages) AS count
FROM person_reportee manager
JOIN person_reportee reportee
ON manager.directly_manages = reportee.pid
WHERE manager.pid = (SELECT id FROM person WHERE name = "fName lName")
GROUP BY directReportees
UNION
SELECT manager.pid AS directReportees, count(L2Reportees.directly_manages) AS count
FROM person_reportee manager
JOIN person_reportee L1Reportees
ON manager.directly_manages = L1Reportees.pid
JOIN person_reportee L2Reportees
ON L1Reportees.directly_manages = L2Reportees.pid
WHERE manager.pid = (SELECT id FROM person WHERE name = "fName lName")
GROUP BY directReportees
) AS T
GROUP BY directReportees)
UNION
(SELECT T.directReportees AS directReportees, sum(T.count) AS count
FROM (
SELECT manager.directly_manages AS directReportees, 0 AS count
FROM person_reportee manager
WHERE manager.pid = (SELECT id FROM person WHERE name = "fName lName")
UNION
SELECT reportee.pid AS directReportees, count(reportee.directly_manages) AS count
FROM person_reportee manager
JOIN person_reportee reportee
ON manager.directly_manages = reportee.pid
WHERE manager.pid = (SELECT id FROM person WHERE name = "fName lName")
GROUP BY directReportees
UNION
SELECT depth2Reportees.pid AS directReportees,
count(depth3Reportees.directly_manages) AS count
FROM person_reportee manager
JOIN person_reportee L1Reportees
ON manager.directly_manages = L1Reportees.pid
JOIN person_reportee L2Reportees
ON L1Reportees.directly_manages = L2Reportees.pid
WHERE manager.pid = (SELECT id FROM person WHERE name = "fName lName")
GROUP BY directReportees
) AS T
GROUP BY directReportees)
UNION
(SELECT T.directReportees AS directReportees, sum(T.count) AS count
    FROM(
    SELECT reportee.directly_manages AS directReportees, 0 AS count
FROM person_reportee manager
JOIN person_reportee reportee
ON manager.directly_manages = reportee.pid
WHERE manager.pid = (SELECT id FROM person WHERE name = "fName lName")
GROUP BY directReportees
UNION
SELECT L2Reportees.pid AS directReportees, count(L2Reportees.directly_manages) AS
count
FROM person_reportee manager
JOIN person_reportee L1Reportees
ON manager.directly_manages = L1Reportees.pid
JOIN person_reportee L2Reportees
ON L1Reportees.directly_manages = L2Reportees.pid
WHERE manager.pid = (SELECT id FROM person WHERE name = "fName lName")
GROUP BY directReportees
) AS T
GROUP BY directReportees)
UNION
(SELECT L2Reportees.directly_manages AS directReportees, 0 AS count
FROM person_reportee manager
JOIN person_reportee L1Reportees
ON manager.directly_manages = L1Reportees.pid
JOIN person_reportee L2Reportees
ON L1Reportees.directly_manages = L2Reportees.pid
WHERE manager.pid = (SELECT id FROM person WHERE name = "fName lName")
)

這種 glue 代碼對(duì)維護(hù)人員和開(kāi)發(fā)者來(lái)說(shuō)就是一場(chǎng)災(zāi)難,沒(méi)有人想寫(xiě)或者去調(diào)試這種代碼,此外,這類(lèi)代碼往往伴隨著嚴(yán)重的性能問(wèn)題,這個(gè)在之后會(huì)詳細(xì)討論。

傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)的性能問(wèn)題

性能問(wèn)題的本質(zhì)在于數(shù)據(jù)分析面臨的數(shù)據(jù)量,假如只查詢(xún)幾十個(gè)節(jié)點(diǎn)或者更少的內(nèi)容,這種操作是完全不需要考慮數(shù)據(jù)庫(kù)性能優(yōu)化的,但當(dāng)節(jié)點(diǎn)數(shù)據(jù)從幾百個(gè)變成幾百萬(wàn)個(gè)甚至幾千萬(wàn)個(gè)后,數(shù)據(jù)庫(kù)性能就成為了整個(gè)產(chǎn)品設(shè)計(jì)的過(guò)程中最需考慮的因素之一。

隨著節(jié)點(diǎn)的增多,用戶(hù)跟用戶(hù)間的關(guān)系,用戶(hù)和產(chǎn)品間的關(guān)系,或者產(chǎn)品和產(chǎn)品間的關(guān)系都會(huì)呈指數(shù)增長(zhǎng)。

以下是一些公開(kāi)的數(shù)據(jù),可以反映數(shù)據(jù)、數(shù)據(jù)和數(shù)據(jù)間關(guān)系的一些實(shí)際情況:

  • 推特:用戶(hù)量為 5 億,用戶(hù)之間存在關(guān)注、點(diǎn)贊關(guān)系

  • 亞馬遜:用戶(hù)量 1.2 億,用戶(hù)和產(chǎn)品間存在購(gòu)買(mǎi)關(guān)系

  • AT&T(美國(guó)三大運(yùn)營(yíng)商之一): 1 億個(gè)號(hào)碼,電話(huà)號(hào)碼間可建立通話(huà)關(guān)系

如下表所示,開(kāi)源的圖數(shù)據(jù)集往往有著上千萬(wàn)個(gè)節(jié)點(diǎn)和上億的邊的數(shù)據(jù):

怎樣分析圖數(shù)據(jù)庫(kù)

在數(shù)據(jù)量這么大的場(chǎng)景中,使用傳統(tǒng) SQL 會(huì)產(chǎn)生很大的性能問(wèn)題,原因主要有兩個(gè):

  1. 大量 JOIN 操作帶來(lái)的開(kāi)銷(xiāo):之前的查詢(xún)語(yǔ)句使用了大量的 JOIN 操作來(lái)找到需要的結(jié)果。而大量的 JOIN 操作在數(shù)據(jù)量很大時(shí)會(huì)有巨大的性能損失,因?yàn)閿?shù)據(jù)本身是被存放在指定的地方,查詢(xún)本身只需要用到部分?jǐn)?shù)據(jù),但是 JOIN 操作本身會(huì)遍歷整個(gè)數(shù)據(jù)庫(kù),這樣就會(huì)導(dǎo)致查詢(xún)效率低到讓人無(wú)法接受。

  2. 反向查詢(xún)帶來(lái)的開(kāi)銷(xiāo):查詢(xún)單個(gè)經(jīng)理的下屬不需要多少開(kāi)銷(xiāo),但是如果我們要去反向查詢(xún)一個(gè)員工的老板,使用表結(jié)構(gòu),開(kāi)銷(xiāo)就會(huì)變得非常大。表結(jié)構(gòu)設(shè)計(jì)得不合理,會(huì)對(duì)后續(xù)的分析、推薦系統(tǒng)產(chǎn)生性能上的影響。比如,當(dāng)關(guān)系從老板 -> 員工 變成 用戶(hù) -> 產(chǎn)品,如果不支持反向查詢(xún),推薦系統(tǒng)的實(shí)時(shí)性就會(huì)大打折扣,進(jìn)而帶來(lái)經(jīng)濟(jì)損失。

下表列出的是一個(gè)非官方的性能測(cè)試(社交網(wǎng)絡(luò)測(cè)試集,一百萬(wàn)用戶(hù),每個(gè)大概有 50 個(gè)好友),體現(xiàn)了在關(guān)系數(shù)據(jù)庫(kù)里,隨著好友查詢(xún)深度的增加而產(chǎn)生的性能變化:

怎樣分析圖數(shù)據(jù)庫(kù)

傳統(tǒng)數(shù)據(jù)庫(kù)的常規(guī)優(yōu)化策略

策略一:索引

索引:SQL 引擎通過(guò)索引來(lái)找到對(duì)應(yīng)的數(shù)據(jù)。

常見(jiàn)的索引包括 B- 樹(shù)索引和哈希索引,建立表的索引是比較常規(guī)的優(yōu)化 SQL 性能的操作。B- 樹(shù)索引簡(jiǎn)單地來(lái)說(shuō)就是給每個(gè)人一個(gè)可排序的獨(dú)立 ID,B- 樹(shù)本身是一個(gè)平衡多叉搜索樹(shù),這個(gè)樹(shù)會(huì)將每個(gè)元素按照索引 ID 進(jìn)行排序,從而支持范圍查找,范圍查找的復(fù)雜度是 O(logN)  ,其中 N 是索引的文件數(shù)目。

但是索引并不能解決所有的問(wèn)題,如果文件更新頻繁或者有很多重復(fù)的元素,就會(huì)導(dǎo)致很大的空間損耗,此外索引的 IO 消耗也值得考慮,索引 IO 尤其是在機(jī)械硬盤(pán)上的 IO 讀寫(xiě)性能上來(lái)說(shuō)非常不理想,常規(guī)的 B- 樹(shù)索引消耗四次 IO 隨機(jī)讀,當(dāng) JOIN 操作變得越來(lái)越多時(shí),硬盤(pán)查找更可能發(fā)生上百次。

策略二:緩存

緩存:緩存主要是為了解決有具有空間或者時(shí)間局域性數(shù)據(jù)的頻繁讀取帶來(lái)的性能優(yōu)化問(wèn)題。一個(gè)比較常見(jiàn)的使用緩存的架構(gòu)是 lookaside cache architecture。下圖是之前 Facebook 用 Memcached  + MySQL 的實(shí)例(現(xiàn)已被 Facebook 自研的圖數(shù)據(jù)庫(kù) TAO 替代):

怎樣分析圖數(shù)據(jù)庫(kù)

在架構(gòu)中,設(shè)計(jì)者假設(shè)用戶(hù)創(chuàng)造的內(nèi)容比用戶(hù)讀取的內(nèi)容要少得多,Memcached 可以簡(jiǎn)單地理解成一個(gè)分布式的支持增刪改查的哈希表,支持上億量級(jí)的用戶(hù)請(qǐng)求。基本的使用流程是當(dāng)客戶(hù)端需讀數(shù)據(jù)時(shí),先查看一下緩存,然后再去查詢(xún) SQL 數(shù)據(jù)庫(kù)。而當(dāng)用戶(hù)需要寫(xiě)入數(shù)據(jù)時(shí),客戶(hù)端先刪除緩存中的 key,讓數(shù)據(jù)過(guò)期,再去更新數(shù)據(jù)庫(kù)。但是這種架構(gòu)有幾個(gè)問(wèn)題:

  • 首先,鍵值緩存對(duì)于圖結(jié)構(gòu)數(shù)據(jù)并不是一個(gè)好的操作語(yǔ)句,每次查詢(xún)一條邊,需要從緩存里把節(jié)點(diǎn)對(duì)應(yīng)的邊全部拿出來(lái);此外,當(dāng)更新一條邊,原來(lái)的所有依賴(lài)邊要被刪除,繼而需要重新加載所有對(duì)應(yīng)邊的數(shù)據(jù),這些都是并發(fā)的性能瓶頸,畢竟實(shí)際場(chǎng)景中一個(gè)點(diǎn)往往伴隨著幾千條邊,這種操作帶來(lái)的時(shí)間、內(nèi)存消耗問(wèn)題不可忽視。

  • 其次,數(shù)據(jù)更新到數(shù)據(jù)讀取有一個(gè)過(guò)程,在上面架構(gòu)中這個(gè)過(guò)程需要主從數(shù)據(jù)庫(kù)跨域通信。原始模型使用了一個(gè)外部標(biāo)識(shí)來(lái)記錄過(guò)期的鍵值對(duì),并且異步地把這些讀取的請(qǐng)求從只讀的從節(jié)點(diǎn)傳遞到主節(jié)點(diǎn),這個(gè)需要跨域通信,延遲相比直接從本地讀大了很多。(類(lèi)似從之前需要走幾百米的距離而現(xiàn)在需要走從北京到深圳的距離)

使用圖結(jié)構(gòu)建模

上述關(guān)系型數(shù)據(jù)庫(kù)建模失敗的主要原因在于數(shù)據(jù)間缺乏內(nèi)在的關(guān)聯(lián)性,針對(duì)這類(lèi)問(wèn)題,更好的建模方式是使用圖結(jié)構(gòu)。

假如數(shù)據(jù)本身就是表格的結(jié)構(gòu),關(guān)系數(shù)據(jù)庫(kù)就可以解決問(wèn)題,但如果你要展示的是數(shù)據(jù)與數(shù)據(jù)間的關(guān)系,關(guān)系數(shù)據(jù)庫(kù)反而不能解決問(wèn)題了,這主要是在查詢(xún)的過(guò)程中不可避免的大量 JOIN 操作導(dǎo)致的,而每次 JOIN 操作卻只用到部分?jǐn)?shù)據(jù),既然反復(fù) JOIN 操作本身會(huì)導(dǎo)致大量的性能損失,如何建模才能更好的解決問(wèn)題呢?答案在點(diǎn)和點(diǎn)之間的關(guān)系上。

點(diǎn)、關(guān)聯(lián)關(guān)系和圖數(shù)據(jù)模型

在我們之前的討論中,傳統(tǒng)數(shù)據(jù)庫(kù)雖然運(yùn)用 JOIN 操作把不同的表鏈接了起來(lái),從而隱式地表達(dá)了數(shù)據(jù)之間的關(guān)系,但是當(dāng)我們要通過(guò) A 管理 B,B 管理 A 的方式查詢(xún)結(jié)果時(shí),表結(jié)構(gòu)并不能直接告訴我們結(jié)果。

如果我們想在做查詢(xún)前就知道對(duì)應(yīng)的查詢(xún)結(jié)果,我們必須先定義節(jié)點(diǎn)和關(guān)系。

節(jié)點(diǎn)和關(guān)系先定義是圖數(shù)據(jù)庫(kù)和別的數(shù)據(jù)庫(kù)的核心區(qū)別。打個(gè)比方,我們可以把經(jīng)理、員工表示成不同的節(jié)點(diǎn),并用一條邊來(lái)代表他們之前存在的管理關(guān)系,或者把用戶(hù)和商品看作節(jié)點(diǎn),用購(gòu)買(mǎi)關(guān)系建模等等。而當(dāng)我們需要新的節(jié)點(diǎn)和關(guān)系時(shí),只需進(jìn)行幾次更新就好,而不用去改變表的結(jié)構(gòu)或者去遷移數(shù)據(jù)。

根據(jù)節(jié)點(diǎn)和關(guān)聯(lián)關(guān)系,之前的數(shù)據(jù)可以根據(jù)下圖所示建模:

怎樣分析圖數(shù)據(jù)庫(kù)

通過(guò)圖數(shù)據(jù)庫(kù) Nebula Graph 原生 nGQL 圖查詢(xún)語(yǔ)言進(jìn)行建模,參考如下操作:

-- Insert People
INSERT VERTEX person(ID, name) VALUES 1:(2020031601, ‘Jeff’);
INSERT VERTEX person(ID, name) VALUES 2:(2020031602, ‘A’);
INSERT VERTEX person(ID, name) VALUES 3:(2020031603, ‘B’);
INSERT VERTEX person(ID, name) VALUES 4:(2020031604, ‘C’);
-- Insert edge
INSERT EDGE manage (level_s, level_end) VALUES 1 -> 2: ('0', '1')
INSERT EDGE manage (level_s, level_end) VALUES 1 -> 3: ('0', '1')
INSERT EDGE manage (level_s, level_end) VALUES 1 -> 4: ('0', '1')

而之前超長(zhǎng)的 query 語(yǔ)句也可以通過(guò) Cypher / nGQL 縮減成短短的 3、4 行代碼。

下面為 nGQL 語(yǔ)句

GO FROM 1 OVER manage YIELD manage.level_s as start_level, manage._dst AS personid
| GO FROM $personid OVER manage where manage.level_s < start_level + 3
YIELD SUM($$.person.id) AS TOTAL, $$.person.name AS list

下面為 Cypher 版本

MATCH (boss)-[:MANAGES*0..3]->(sub),
(sub)-[:MANAGES*1..3]->(personid)
WHERE boss.name = “Jeff”
RETURN sub.name AS list, count(personid) AS Total

從近百行代碼變成 3、4 行代碼可以明顯地看出圖數(shù)據(jù)庫(kù)在數(shù)據(jù)表達(dá)能力上的優(yōu)勢(shì)。

圖數(shù)據(jù)庫(kù)性能優(yōu)化

圖數(shù)據(jù)庫(kù)本身對(duì)高度連接、結(jié)構(gòu)性不強(qiáng)的數(shù)據(jù)做了專(zhuān)門(mén)優(yōu)化。不同的圖數(shù)據(jù)庫(kù)根據(jù)不同的場(chǎng)景也做了針對(duì)性?xún)?yōu)化,筆者在這里簡(jiǎn)單介紹以下幾種圖數(shù)據(jù)庫(kù),BTW,這些圖數(shù)據(jù)庫(kù)都支持原生圖建模。

Neo4j

Neo4j 是最知名的一種圖數(shù)據(jù)庫(kù),在業(yè)界有微軟、ebay 在用 Neo4j 來(lái)解決部分業(yè)務(wù)場(chǎng)景,Neo4j 的性能優(yōu)化有兩點(diǎn),一個(gè)是原生圖數(shù)據(jù)處理上的優(yōu)化,一個(gè)是運(yùn)用了 LRU-K 緩存來(lái)緩存數(shù)據(jù)。

原生圖數(shù)據(jù)處理優(yōu)化

我們說(shuō)一個(gè)圖數(shù)據(jù)庫(kù)支持原生圖數(shù)據(jù)處理就代表這個(gè)數(shù)據(jù)庫(kù)有能力去支持 index-free adjacency。

index-free adjancency 就是每個(gè)節(jié)點(diǎn)會(huì)保留連接節(jié)點(diǎn)的引用,從而這個(gè)節(jié)點(diǎn)本身就是連接節(jié)點(diǎn)的一個(gè)索引,這種操作的性能比使用全局索引好很多,同時(shí)假如我們根據(jù)圖來(lái)進(jìn)行查詢(xún),這種查詢(xún)是與整個(gè)圖的大小無(wú)關(guān)的,只與查詢(xún)節(jié)點(diǎn)關(guān)聯(lián)邊的數(shù)目有關(guān),如果用 B 樹(shù)索引進(jìn)行查詢(xún)的復(fù)雜度是 O(logN),使用這種結(jié)構(gòu)查詢(xún)的復(fù)雜度就是 O(1)。當(dāng)我們要查詢(xún)多層數(shù)據(jù)時(shí),查詢(xún)所需要的時(shí)間也不會(huì)隨著數(shù)據(jù)集的變大而呈現(xiàn)指數(shù)增長(zhǎng),反而會(huì)是一個(gè)比較穩(wěn)定的常數(shù),畢竟每次查詢(xún)只會(huì)根據(jù)對(duì)應(yīng)的節(jié)點(diǎn)找到連接的邊而不會(huì)去遍歷所有的節(jié)點(diǎn)。

主存緩存優(yōu)化

在 2.2 版本的 Neo4j 中使用了 LRU-K 緩存,這種緩存簡(jiǎn)而言之就是將使用頻率最低的頁(yè)面從緩存中彈出,青睞使用頻率更高的頁(yè)面,這種設(shè)計(jì)保證在統(tǒng)計(jì)意義上的緩存資源使用最優(yōu)化。

JanusGraph

JanusGraph 本身并沒(méi)有關(guān)注于去實(shí)現(xiàn)存儲(chǔ)和分析,而是實(shí)現(xiàn)了圖數(shù)據(jù)庫(kù)引擎與多種索引和存儲(chǔ)引擎的接口,利用這些接口來(lái)實(shí)現(xiàn)數(shù)據(jù)和存儲(chǔ)和索引。JanusGraph 主要目的是在原來(lái)框架的基礎(chǔ)上支持圖數(shù)據(jù)的建模同時(shí)優(yōu)化圖數(shù)據(jù)序列化、圖數(shù)據(jù)建模、圖數(shù)據(jù)執(zhí)行相關(guān)的細(xì)節(jié)。JanusGraph 提供了模塊化的數(shù)據(jù)持久化、數(shù)據(jù)索引和客戶(hù)端的接口,從而更方便地將圖數(shù)據(jù)模型運(yùn)用到實(shí)際開(kāi)發(fā)中。

此外,JanusGraph 支持用 Cassandra、HBase、BerkelyDB 作為存儲(chǔ)引擎,支持使用 ElasticSearch、Solr 還有 Lucene 進(jìn)行數(shù)據(jù)索引。

在應(yīng)用方面,可以用兩種方式與 JanusGraph 進(jìn)行交互:

  • 將 JanusGraph 變成應(yīng)用的一部分進(jìn)行查詢(xún)、緩存,并且這些數(shù)據(jù)交互都是在同一臺(tái) JVM 上執(zhí)行,但數(shù)據(jù)的來(lái)源可能在本地或者在別的地方。

  • 將 JanusGraph 作為一個(gè)服務(wù),讓客戶(hù)端與服務(wù)端分離,同時(shí)客戶(hù)端提交 Gremlin 查詢(xún)語(yǔ)句到服務(wù)器上執(zhí)行對(duì)應(yīng)的數(shù)據(jù)處理操作。

怎樣分析圖數(shù)據(jù)庫(kù)

Nebula Graph

下面簡(jiǎn)單地介紹了一下 Nebula Graph 的系統(tǒng)設(shè)計(jì)。

使用 KV 對(duì)來(lái)進(jìn)行圖數(shù)據(jù)處理

Nebula Graph 使用了 vertexID + TagID 作為鍵在不同的 partition 間存儲(chǔ) in-key 和 out-key 相關(guān)的數(shù)據(jù),這種操作可以確保在大規(guī)模集群上的高可用,使用分布式的 partition 和 sharding 也增加了 Nebula Graph 的吞吐量和容錯(cuò)的能力。

Shared-noting 分布式存儲(chǔ)層

Storage Service 采用 shared-nothing 的分布式架構(gòu)設(shè)計(jì),每個(gè)存儲(chǔ)節(jié)點(diǎn)都有多個(gè)本地 KV 存儲(chǔ)實(shí)例作為物理存儲(chǔ)。Nebula 采用多數(shù)派協(xié)議 Raft 來(lái)保證這些 KV 存儲(chǔ)之間的一致性(由于 Raft 比 Paxo 更簡(jiǎn)潔,我們選用了 Raft)。在 KVStore 之上是圖語(yǔ)義層,用于將圖操作轉(zhuǎn)換為下層 KV 操作。

圖數(shù)據(jù)(點(diǎn)和邊)通過(guò) Hash 的方式存儲(chǔ)在不同 partition 中。這里用的 Hash 函數(shù)實(shí)現(xiàn)很直接,即 vertex_id 取余 partition 數(shù)。在 Nebula Graph 中,partition 表示一個(gè)虛擬的數(shù)據(jù)集,這些 partition 分布在所有的存儲(chǔ)節(jié)點(diǎn),分布信息存儲(chǔ)在 Meta Service 中(因此所有的存儲(chǔ)節(jié)點(diǎn)和計(jì)算節(jié)點(diǎn)都能獲取到這個(gè)分布信息)。

無(wú)狀態(tài)計(jì)算層

每個(gè)計(jì)算節(jié)點(diǎn)都運(yùn)行著一個(gè)無(wú)狀態(tài)的查詢(xún)計(jì)算引擎,而節(jié)點(diǎn)彼此間無(wú)任何通信關(guān)系。計(jì)算節(jié)點(diǎn)僅從 Meta Service 讀取 meta 信息,以及和 Storage Service 進(jìn)行交互。這樣設(shè)計(jì)使得計(jì)算層集群更容易使用 K8s 管理或部署在云上。

計(jì)算層的負(fù)載均衡有兩種形式,最常見(jiàn)的方式是在計(jì)算層上加一個(gè)負(fù)載均衡(balance),第二種方法是將計(jì)算層所有節(jié)點(diǎn)的 IP 地址配置在客戶(hù)端中,這樣客戶(hù)端可以隨機(jī)選取計(jì)算節(jié)點(diǎn)進(jìn)行連接。

每個(gè)查詢(xún)計(jì)算引擎都能接收客戶(hù)端的請(qǐng)求,解析查詢(xún)語(yǔ)句,生成抽象語(yǔ)法樹(shù)(AST)并將 AST 傳遞給執(zhí)行計(jì)劃器和優(yōu)化器,最后再交由執(zhí)行器執(zhí)行。

圖數(shù)據(jù)庫(kù)是當(dāng)今的趨勢(shì)

在當(dāng)今,圖數(shù)據(jù)庫(kù)收到了更多分析師和咨詢(xún)公司的關(guān)注

Graph analysis is possibly the single most effective  competitive differentiator for organizations  pursuing data-driven operations and  decisions after the design of data capture.        ———————Gartner

“Graph analysis is the true killer app for Big Data.”      ——————————Forrester

同時(shí)圖數(shù)據(jù)庫(kù)在 DB-Ranking 上的排名也呈現(xiàn)出上升最快的趨勢(shì),可見(jiàn)需求之迫切:

怎樣分析圖數(shù)據(jù)庫(kù)

圖數(shù)據(jù)庫(kù)實(shí)踐:不僅僅是社交網(wǎng)絡(luò)

Netflix 云數(shù)據(jù)庫(kù)的工程實(shí)踐

怎樣分析圖數(shù)據(jù)庫(kù)

Netflix 采用了JanusGraph + Cassandra + ElasticSearch 作為自身的圖數(shù)據(jù)庫(kù)架構(gòu),他們運(yùn)用這種架構(gòu)來(lái)做數(shù)字資產(chǎn)管理。

節(jié)點(diǎn)表示數(shù)字產(chǎn)品比如電影、紀(jì)錄片等,同時(shí)這些產(chǎn)品之間的關(guān)系就是節(jié)點(diǎn)間的邊。

當(dāng)前的 Netflix 有大概 2 億的節(jié)點(diǎn),70 多種數(shù)字產(chǎn)品,每分鐘都有上百條的 query 和數(shù)據(jù)更新。

此外,Netflix 也把圖數(shù)據(jù)庫(kù)運(yùn)用在了授權(quán)、分布式追蹤、可視化工作流上。比如可視化 Git 的 commit,jenkins 部署這些工作。

Adobe 的技術(shù)迭代

一般而言,新技術(shù)往往在開(kāi)始的時(shí)候大都不被大公司所青睞,圖數(shù)據(jù)庫(kù)并沒(méi)有例外,大公司本身有很多的遺留項(xiàng)目,而這些項(xiàng)目本身的用戶(hù)體量和使用需求又讓這些公司不敢冒著風(fēng)險(xiǎn)來(lái)使用新技術(shù)去改變這些處于穩(wěn)定的產(chǎn)品。Adobe 在這里做了一個(gè)迭代新技術(shù)的例子,用 Neo4j 圖數(shù)據(jù)庫(kù)替換了舊的 NoSQL Cassandra 數(shù)據(jù)庫(kù)。

這個(gè)被大改的系統(tǒng)名字叫 Behance,是 Adobe 在 15 年發(fā)布的一個(gè)內(nèi)容社交平臺(tái),有大概 1 千萬(wàn)的用戶(hù),在這里人們可以分享自己的創(chuàng)作給百萬(wàn)人看。

怎樣分析圖數(shù)據(jù)庫(kù)

這樣一個(gè)巨大的遺留系統(tǒng)本來(lái)是通過(guò) Cassandra 和 MongoDB 搭建的,基于歷史遺留問(wèn)題,系統(tǒng)有不少的性能瓶頸不得不解決。

MongoDB 和 Cassandra 的讀取性能慢主要因?yàn)樵鹊南到y(tǒng)設(shè)計(jì)采用了 fan-out 的設(shè)計(jì)模式——受關(guān)注多的用戶(hù)發(fā)表的內(nèi)容會(huì)單獨(dú)分發(fā)給每個(gè)讀者,這種設(shè)計(jì)模式也導(dǎo)致了網(wǎng)絡(luò)架構(gòu)的大延遲,此外 Cassandra 本身的運(yùn)維也需要不小的技術(shù)團(tuán)隊(duì),這也是一個(gè)很大的問(wèn)題。

怎樣分析圖數(shù)據(jù)庫(kù)

在這里為了搭建一個(gè)靈活、高效、穩(wěn)定的系統(tǒng)來(lái)提供消息 feeding 并最小化數(shù)據(jù)存儲(chǔ)的規(guī)模,Adobe 決定遷移原本的 Cassandra 數(shù)據(jù)庫(kù)到 Neo4j 圖數(shù)據(jù)庫(kù)。

在 Neo4j 圖數(shù)據(jù)庫(kù)中采用一種所謂的 Tiered relationships 來(lái)表示用戶(hù)之間的關(guān)系,這個(gè)邊的關(guān)系可以去定義不同的訪問(wèn)狀態(tài),比如:僅部分用戶(hù)可見(jiàn),僅關(guān)注者可見(jiàn)這些基本操作。

數(shù)據(jù)模型如圖所示

怎樣分析圖數(shù)據(jù)庫(kù)

使用這種數(shù)據(jù)模型并使用 Leader-follower 架構(gòu)來(lái)優(yōu)化讀寫(xiě),這個(gè)平臺(tái)獲得了巨大的性能提升:

怎樣分析圖數(shù)據(jù)庫(kù)

  1. 運(yùn)維需求的時(shí)長(zhǎng)在使用了 Neo4j 以后下降了 300%。

  2. 存儲(chǔ)需求降低了 1000 倍, Neo4j 僅需 50G 存儲(chǔ)數(shù)據(jù), 而 Cassandra 需要 50TB。

  3. 僅僅需要 3 個(gè)服務(wù)實(shí)例就可以支持整個(gè)服務(wù)器的流暢運(yùn)行,之前則需要 48 個(gè)。

  4. 圖數(shù)據(jù)庫(kù)本身就提供了更高的可擴(kuò)展性。

在當(dāng)今的大數(shù)據(jù)時(shí)代,采用圖數(shù)據(jù)庫(kù)可以用小成本在原有架構(gòu)上獲得巨大的性能提升。圖數(shù)據(jù)庫(kù)不僅僅可以在 5G、AI、物聯(lián)網(wǎng)領(lǐng)域發(fā)揮巨大的推動(dòng)作用,同時(shí)也可以用來(lái)重構(gòu)原本的遺留系統(tǒng)。

雖然不同的圖數(shù)據(jù)庫(kù)可能有著截然不同的底層實(shí)現(xiàn),但這些都完全支持用圖的方式來(lái)構(gòu)建數(shù)據(jù)模型從而讓不同的組件之間相互聯(lián)系,從我們之前的討論來(lái)看,這一種數(shù)據(jù)模型層次的改變會(huì)極大地簡(jiǎn)化很多日常數(shù)據(jù)系統(tǒng)中所面臨的問(wèn)題,增大系統(tǒng)的吞吐量并且降低運(yùn)維的需求。

看完上述內(nèi)容,你們掌握怎樣分析圖數(shù)據(jù)庫(kù)的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI