隨著社交、電商、金融、零售、物聯(lián)網(wǎng)等行業(yè)的快速發(fā)展,現(xiàn)實(shí)社會(huì)織起了了一張龐大而復(fù)雜的關(guān)系網(wǎng),傳統(tǒng)數(shù)據(jù)庫(kù)很難處理關(guān)系運(yùn)算。大數(shù)據(jù)行業(yè)需要處理的數(shù)據(jù)之間的關(guān)系隨數(shù)據(jù)量呈幾何級(jí)數(shù)增長(zhǎng),亟需一種支持海量復(fù)雜數(shù)據(jù)關(guān)系運(yùn)算的數(shù)據(jù)庫(kù),
圖數(shù)據(jù)庫(kù)應(yīng)運(yùn)而生。
1. 圖數(shù)據(jù)簡(jiǎn)介
1.1 何為圖?
學(xué)過(guò)數(shù)據(jù)結(jié)構(gòu)這么課程的同學(xué)腦海中應(yīng)該或多或少有 圖的概念。
形式上,圖是頂點(diǎn)(Vertices)和邊(Edge)的集合,用以表現(xiàn)節(jié)點(diǎn)間的接關(guān)聯(lián)關(guān)系;
在圖數(shù)據(jù)庫(kù)的建模中用節(jié)點(diǎn)表示實(shí)體,用邊表現(xiàn)關(guān)系。

在我們的現(xiàn)實(shí)生活中,圖表示的關(guān)系可以說(shuō)是無(wú)處不在。 Gartner定義了商業(yè)世界中5個(gè)最重要的圖——社交、意向、消費(fèi)、興趣和移動(dòng),并認(rèn)為運(yùn)用這些圖的能力是一個(gè)項(xiàng)”可持續(xù)的競(jìng)爭(zhēng)優(yōu)勢(shì)“
1.2 圖數(shù)據(jù)庫(kù) GraphDB
圖數(shù)據(jù)庫(kù)(Graph database)并非指存儲(chǔ)圖片的數(shù)據(jù)庫(kù),而是以 圖這種數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)和查詢數(shù)據(jù)。
圖形數(shù)據(jù)庫(kù)是一種在線數(shù)據(jù)庫(kù)管理系統(tǒng),具有處理圖形數(shù)據(jù)模型的創(chuàng)建,讀取,更新和刪除(CRUD)操作。
與其他數(shù)據(jù)庫(kù)不同,關(guān)系在圖數(shù)據(jù)庫(kù)中占首要地位。這意味著應(yīng)用程序不必使用外鍵或帶外處理(如MapReduce)來(lái)推斷數(shù)據(jù)的關(guān)聯(lián)關(guān)系。
與關(guān)系數(shù)據(jù)庫(kù)或其他NoSQL數(shù)據(jù)庫(kù)相比,圖數(shù)據(jù)庫(kù)的數(shù)據(jù)模型也更加簡(jiǎn)單,更具表現(xiàn)力。圖形數(shù)據(jù)庫(kù)是為與事務(wù)(OLTP)系統(tǒng)一起使用而構(gòu)建的,并且在設(shè)計(jì)時(shí)考慮了事務(wù)完整性和操作可用性。
根據(jù)存儲(chǔ)和處理模型不同,市面上圖數(shù)據(jù)庫(kù)也有一些區(qū)分。
1.2.1 圖存儲(chǔ)
一些圖數(shù)據(jù)庫(kù)使用 原生圖存儲(chǔ),這類存儲(chǔ)是經(jīng)過(guò)優(yōu)化的,并且是專門為了存儲(chǔ)和管理圖而設(shè)計(jì)的。并不是所有圖數(shù)據(jù)庫(kù)都是使用原生圖存儲(chǔ),也有一些圖數(shù)據(jù)庫(kù)將圖數(shù)據(jù)序列化,然后保存到關(guān)系型數(shù)據(jù)庫(kù)或者面向?qū)ο髷?shù)據(jù)庫(kù),或其他通用數(shù)據(jù)存儲(chǔ)中。
1.2.2 圖處理引擎
原生圖處理(也稱為 無(wú)索引鄰接)是處理圖數(shù)據(jù)的最有效方法,因?yàn)檫B接的節(jié)點(diǎn)在數(shù)據(jù)庫(kù)中物理地指向彼此。非本機(jī)圖處理使用其他方法來(lái)處理CRUD操作。

比如:
Neo4J就是屬于原生圖數(shù)據(jù)庫(kù),它使用的后端存儲(chǔ)是專門為Neo4J這種圖數(shù)據(jù)庫(kù)定制和優(yōu)化的,理論上說(shuō)能更有利于發(fā)揮圖數(shù)據(jù)庫(kù)的性能。AllegroGraph不是原生圖數(shù)據(jù)庫(kù),而將數(shù)據(jù)存儲(chǔ)在其他成熟的非圖后端系統(tǒng)上,比如Mysql或者Hbase。
1.3 應(yīng)用場(chǎng)景
世界上很多著名的公司都在使用圖數(shù)據(jù)庫(kù)。比如:
社交領(lǐng)域:Facebook, Twitter,Linkedin用它來(lái)管理社交關(guān)系,實(shí)現(xiàn)好友推薦
零售領(lǐng)域:eBay,沃爾瑪使用它實(shí)現(xiàn)商品實(shí)時(shí)推薦,給買家更好的購(gòu)物體驗(yàn)
金融領(lǐng)域:摩根大通,花旗和瑞銀等銀行在用圖數(shù)據(jù)庫(kù)做風(fēng)控處理
汽車制造領(lǐng)域:沃爾沃,戴姆勒和豐田等頂級(jí)汽車制造商依靠圖數(shù)據(jù)庫(kù)推動(dòng)創(chuàng)新制造解決方案
電信領(lǐng)域:Verizon, Orange和AT&T 等電信公司依靠圖數(shù)據(jù)庫(kù)來(lái)管理網(wǎng)絡(luò),控制訪問(wèn)并支持客戶360
酒店領(lǐng)域:萬(wàn)豪和雅高酒店等頂級(jí)酒店公司依使用圖數(shù)據(jù)庫(kù)來(lái)管理復(fù)雜且快速變化的庫(kù)存
2. 圖數(shù)據(jù) In NoSQL
NoSQL數(shù)據(jù)庫(kù)大致可以分為四類,而圖數(shù)據(jù)庫(kù)正是其一,各種圖數(shù)據(jù)庫(kù)對(duì)比,請(qǐng)見(jiàn)下方圖表。
| 分類 | 數(shù)據(jù)模型 | 優(yōu)勢(shì) | 劣勢(shì) | 舉例 |
|---|---|---|---|---|
| 鍵值數(shù)據(jù)庫(kù) | 哈希表 | 查找速度快 | 數(shù)據(jù)無(wú)結(jié)構(gòu)化,通常只被當(dāng)作字符串或者二進(jìn)制數(shù)據(jù) | Redis |
| 列存儲(chǔ)數(shù)據(jù)庫(kù) | 列式數(shù)據(jù)存儲(chǔ) | 查找速度快;支持分布橫向擴(kuò)展;數(shù)據(jù)壓縮率高 | 功能相對(duì)受限 | HBase |
| 文檔型數(shù)據(jù)庫(kù) | 鍵值對(duì)擴(kuò)展 | 數(shù)據(jù)結(jié)構(gòu)要求不嚴(yán)格;表結(jié)構(gòu)可變;不需要預(yù)先定義表結(jié)構(gòu) | 查詢性能不高,缺乏統(tǒng)一的查詢語(yǔ)法 | MongoDB |
| 圖數(shù)據(jù)庫(kù) | 節(jié)點(diǎn)和關(guān)系組成的圖 | 利用圖結(jié)構(gòu)相關(guān)算法(最短路徑、節(jié)點(diǎn)度關(guān)系查找等) | 可能需要對(duì)整個(gè)圖做計(jì)算,不利于圖數(shù)據(jù)分布存儲(chǔ) | Neo4j |
3. 圖數(shù)據(jù)庫(kù) VS 關(guān)系型數(shù)據(jù)庫(kù)
與關(guān)系型數(shù)據(jù)庫(kù)和 NoSQL 數(shù)據(jù)庫(kù)相比,定義良好的圖數(shù)據(jù)庫(kù)都有著明顯優(yōu)勢(shì)。具體如下
- 性能: 與關(guān)系型數(shù)據(jù)庫(kù)和NoSQL存儲(chǔ)處理關(guān)聯(lián)數(shù)據(jù)相比,選擇圖數(shù)據(jù)庫(kù)會(huì)有絕對(duì)的性能提升。隨著數(shù)據(jù)集的不斷增大,關(guān)系型數(shù)據(jù)庫(kù)處理密集join(join-intensive)查詢的性能也會(huì)隨之變差,而圖數(shù)據(jù)庫(kù)則不然。在數(shù)據(jù)集增大時(shí),它的性能趨向于保持不變,這是因?yàn)椴樵兛偸侵慌c圖的一部分相關(guān)。因此,每個(gè)查詢的執(zhí)行時(shí)間只和滿足查詢條件的那部分遍歷的圖的大?。ǘ皇钦麄€(gè)圖的大?。┏烧?。
- 靈活: 圖天生就是可擴(kuò)展的,這意味著我們可以對(duì)一個(gè)已經(jīng)存在的結(jié)構(gòu)增加不同種類的聯(lián)系、新節(jié)點(diǎn)和新子圖,而不用擔(dān)心破壞已有的查詢或應(yīng)用程序的功能。這些特點(diǎn)對(duì)于開(kāi)發(fā)者的生產(chǎn)力和項(xiàng)目風(fēng)險(xiǎn)一般都有積極的意義。同時(shí)由于圖模型的靈活性,我們不必在項(xiàng)目最初就窮思竭慮地把領(lǐng)域中的每一個(gè)細(xì)枝末節(jié)都考慮到模型中—這種做法在不斷變化的業(yè)務(wù)需求面前,簡(jiǎn)直就是蠻干。圖的天然可擴(kuò)展性也意味著我們會(huì)做更少的數(shù)據(jù)遷移,從而降低維護(hù)開(kāi)銷和風(fēng)險(xiǎn)。
- 敏捷性: 因?yàn)閳D數(shù)據(jù)庫(kù)不需要schema,所以它缺少以schema為導(dǎo)向的數(shù)據(jù)管理機(jī)制,即在關(guān)系世界中我們已經(jīng)熟知的機(jī)制。但這并不是一個(gè)風(fēng)險(xiǎn),相反,它促使我們采用了一種更可見(jiàn)的、可操作的管理方式。正如我們?cè)诘?章會(huì)講到的,圖數(shù)據(jù)庫(kù)的管理通常作用于編程方式,利用測(cè)試來(lái)驅(qū)動(dòng)數(shù)據(jù)模型和查詢,以及依靠圖來(lái)斷言業(yè)務(wù)規(guī)則。這不再是一個(gè)有爭(zhēng)議的做法,事實(shí)上這已經(jīng)比關(guān)系型開(kāi)發(fā)應(yīng)用更廣了。圖數(shù)據(jù)庫(kù)開(kāi)發(fā)方式非常符合當(dāng)今的敏捷軟件開(kāi)發(fā)和測(cè)試驅(qū)動(dòng)軟件開(kāi)發(fā)實(shí)踐,這使得以圖數(shù)據(jù)庫(kù)為后端的應(yīng)用程序可以跟上不斷變化的業(yè)務(wù)環(huán)境。
4. 關(guān)系查詢性能對(duì)比
在數(shù)據(jù)關(guān)系中心,圖形數(shù)據(jù)庫(kù)在查詢速度方面非常高效,即使對(duì)于深度和復(fù)雜的查詢也是如此。在《Neo4j in Action》這本書(shū)中,作者在關(guān)系型數(shù)據(jù)庫(kù)
和圖數(shù)據(jù)庫(kù)(Neo4j)之間進(jìn)行了實(shí)驗(yàn)。
他們的實(shí)驗(yàn)試圖在一個(gè)社交網(wǎng)絡(luò)里找到最大深度為5的朋友的朋友。他們的數(shù)據(jù)集包括100萬(wàn)人,每人約有50個(gè)朋友。實(shí)驗(yàn)結(jié)果如下:
| 深度 | MySQL執(zhí)行時(shí)間(s) | Neo4J執(zhí)行時(shí)間(s) | 返回記錄數(shù) |
|---|---|---|---|
| 2 | 0.016 | 0.01 | ~2500 |
| 3 | 30.267 | 0.168 | ~110 000 |
| 4 | 1543.505 | 1.359 | ~600 000 |
| 5 | 未完成 | 2.132 | ~800 000 |
在深度為2時(shí)(即朋友的朋友),兩種數(shù)據(jù)庫(kù)性能相差不是很明顯;深度為3時(shí)(即朋友的朋友的朋友),很明顯,關(guān)系型數(shù)據(jù)庫(kù)的響應(yīng)時(shí)間30s,已經(jīng)變得不可接受了;深度到4時(shí),關(guān)系數(shù)據(jù)庫(kù)需要近半個(gè)小時(shí)才能返回結(jié)果,使其無(wú)法應(yīng)用于在線系統(tǒng);深度到5時(shí),關(guān)系型數(shù)據(jù)庫(kù)已經(jīng)無(wú)法完成查詢。而對(duì)于圖數(shù)據(jù)庫(kù)Neo4J,深度從3到5,其響應(yīng)時(shí)間均在3秒以內(nèi)。
可以看出,對(duì)于圖數(shù)據(jù)庫(kù)來(lái)說(shuō),數(shù)據(jù)量越大,越復(fù)雜的關(guān)聯(lián)查詢,約有利于體現(xiàn)其優(yōu)勢(shì)。從深度為4/5的查詢結(jié)果我們可以看出,圖數(shù)據(jù)庫(kù)返回了整個(gè)社交網(wǎng)絡(luò)一半以上的人數(shù)。
5. 主流數(shù)據(jù)圖產(chǎn)品
根據(jù)DB-Engines最新發(fā)布的圖數(shù)據(jù)庫(kù)排名:

- Neo4j 業(yè)界老大,行業(yè)標(biāo)桿,江湖一哥;
- Nebula 異軍突起,厚積薄發(fā),當(dāng)紅新銳;
二者搜索熱度對(duì)比: https://db-engines.com/en/system/Nebula+Graph%3BNeo4j

具體特性對(duì)比:

對(duì)比總結(jié)
- 性能與成本:Neo4j開(kāi)源版本是單機(jī)的,最少2G內(nèi)存就可以支持億級(jí)別的數(shù)據(jù);Nebula性能突出但是維護(hù)成本較高,很吃資源(測(cè)試環(huán)境需要至少8G內(nèi)存才能啟動(dòng));
- 學(xué)習(xí)成本:Neo4j有專屬查詢語(yǔ)言“Cypher”,Nebula也有自己的“NQL”,二者都需要學(xué)習(xí)
- 開(kāi)發(fā)成本:Neo4j和Spring緊密合作,對(duì)于Java生態(tài)支持的更好;Nebula 在這方面剛剛起步
6. Cypher查詢語(yǔ)言
Cypher是Neo4j的圖形查詢語(yǔ)言,允許用戶存儲(chǔ)和檢索圖形數(shù)據(jù)庫(kù)中的數(shù)據(jù)。
比如下面我們直接通過(guò)代碼來(lái)對(duì)比下 MySQL 和 Neo4j 里面的查詢,我相信即使不寫(xiě)任何注釋,即使第一次接觸 Neo4j 的人也能輕輕松松的看懂這些查詢語(yǔ)句。
<!-- 1. 全表掃描 -->
<!-- mysql -->
SELECT p.*
FROM products as p;
<!-- neo4j -->
MATCH (p:Product)
RETURN p;
<!-- 2. 查詢價(jià)格最貴的10個(gè)商品,只返回商品名字和單價(jià) -->
<!-- mysql -->
SELECT p.ProductName, p.UnitPrice
FROM products as p
ORDER BY p.UnitPrice DESC
LIMIT 10;
<!-- neo4j -->
MATCH (p:Product)
RETURN p.productName, p.unitPrice
ORDER BY p.unitPrice DESC
LIMIT 10;
<!-- 3. 按照商品名字篩選 -->
<!-- mysql -->
SELECT p.ProductName, p.UnitPrice
FROM products AS p
WHERE p.ProductName = 'Chocolade';
<!-- neo4j -->
MATCH (p:Product)
WHERE p.productName = "Chocolade"
RETURN p.productName, p.unitPrice;
<!-- 其他的寫(xiě)法 -->
MATCH (p:Product {productName:"Chocolade"})
RETURN p.productName, p.unitPrice;
<!-- 4. 按照商品名字篩選2 -->
<!-- mysql -->
SELECT p.ProductName, p.UnitPrice
FROM products as p
WHERE p.ProductName IN ('Chocolade','Chai');
<!-- neo4j -->
MATCH (p:Product)
WHERE p.productName IN ['Chocolade','Chai']
RETURN p.productName, p.unitPrice;
<!-- 5. 模糊查詢和數(shù)值過(guò)濾 -->
<!-- mysql -->
SELECT p.ProductName, p.UnitPrice
FROM products AS p
WHERE p.ProductName LIKE 'C%' AND p.UnitPrice > 100;
<!-- neo4j -->
MATCH (p:Product)
WHERE p.productName STARTS WITH "C" AND p.unitPrice > 100
RETURN p.productName, p.unitPrice;
<!-- 6. 多表聯(lián)合查詢-->
<!-- mysql -->
SELECT DISTINCT c.CompanyName
FROM customers AS c
JOIN orders AS o ON (c.CustomerID = o.CustomerID)
JOIN order_details AS od ON (o.OrderID = od.OrderID)
JOIN products AS p ON (od.ProductID = p.ProductID)
WHERE p.ProductName = 'Chocolade';
<!-- neo4j -->
MATCH (p:Product {productName:"Chocolade"})<-[:PRODUCT]-(:Order)<-[:PURCHASED]-(c:Customer)
RETURN distinct c.companyName;
<!-- 7. -->
<!-- mysql -->
SELECT e.EmployeeID, count(*) AS Count
FROM Employee AS e
JOIN Order AS o ON (o.EmployeeID = e.EmployeeID)
GROUP BY e.EmployeeID
ORDER BY Count DESC LIMIT 10;
<!-- neo4j -->
MATCH (:Order)<-[:SOLD]-(e:Employee)
RETURN e.name, count(*) AS cnt
ORDER BY cnt DESC LIMIT 10
下面在看一些更為復(fù)雜的例子,我們要查找Joe的所以二度好友:

MATCH
(person:Person)-[:KNOWS]-(friend:Person)-[:KNOWS]-
(foaf:Person)
WHERE
person.name = "Joe"
AND NOT (person)-[:KNOWS]-(foaf)
RETURN
foaf
// Joe認(rèn)識(shí)Sally,Sally認(rèn)識(shí)Anna。Bob被排除在結(jié)果之外,因?yàn)槌送ㄟ^(guò)Sally成為二級(jí)朋友之外,他還是一級(jí)朋友。
圖數(shù)據(jù)庫(kù)會(huì)經(jīng)常處理樹(shù)形結(jié)構(gòu)數(shù)據(jù),必須要遍歷Block樹(shù)的最大深度
match (n:Block)
where (n)-[:OWN]->() and not ()-[:OWN]->(n) // 找到根節(jié)點(diǎn)
match p = (n)-[:OWN*1..]->(m) //查詢所有從根節(jié)點(diǎn)出發(fā)的路徑
return p, length(p) as L
order by L desc // 找到路徑長(zhǎng)度最大的
limit 1
或者是要從固定節(jié)點(diǎn)(aid = 1000)向上尋找到另一個(gè)節(jié)點(diǎn)(aid=10)的最短路徑
match p = shortestPath( (e:block {aid:10000}) <-[*0..1000]- (r:block {aid:1}) )
return p;
更多cypher語(yǔ)法請(qǐng)閱讀官方文檔: Cypher語(yǔ)法
7. 總結(jié)
圖數(shù)據(jù)庫(kù)應(yīng)對(duì)的是當(dāng)今一個(gè)宏觀的商業(yè)世界的大趨勢(shì):憑借高度關(guān)聯(lián)、復(fù)雜的動(dòng)態(tài)數(shù)據(jù),獲得洞察力和競(jìng)爭(zhēng)優(yōu)勢(shì)。國(guó)內(nèi)越來(lái)越多的公司開(kāi)始進(jìn)入圖數(shù)據(jù)庫(kù)領(lǐng)域,研發(fā)自己的圖數(shù)據(jù)庫(kù)系統(tǒng)。對(duì)于任何達(dá)到一定規(guī)?;騼r(jià)值的數(shù)據(jù),圖數(shù)據(jù)庫(kù)都是呈現(xiàn)和查詢這些關(guān)系數(shù)據(jù)的最好方式。而理解和分析這些圖的能力將成為企業(yè)未來(lái)最核心的競(jìng)爭(zhēng)力。
更多學(xué)習(xí)資料:
- 如何使用Neoj4建模
- Cypher語(yǔ)法
- 深度運(yùn)算法 https://ramona-chen.top/2020/03/24/neo4j-cha-xun-duo-shen-du-guan-xi-jie-dian/
- Neo4j Java Driver https://neo4j.com/docs/developer-manual/current/drivers/get-started/
- Spring 實(shí)體映射關(guān)系 https://github.com/neo4j/neo4j-ogm
- 官方學(xué)習(xí)數(shù)據(jù) https://neo4j.com/graphgists/
- 運(yùn)維 Shell 腳本命令 https://neo4j.com/docs/operations-manual/4.3/tools/cypher-shell/