App下載

圖數(shù)據(jù)庫 VS 關(guān)系數(shù)據(jù)庫——了解圖數(shù)據(jù)庫的工作原理

暮色上濃妝 2021-09-10 10:34:15 瀏覽數(shù) (6765)
反饋

圖數(shù)據(jù)庫越來越受歡迎和被采用。隨著來自許多不同來源的數(shù)據(jù)量越來越大,能夠理解數(shù)據(jù)并了解它們之間的聯(lián)系至關(guān)重要?;蛟S有些小伙伴可能聽說過圖數(shù)據(jù)庫 (GDB),有些人可能還沒有聽說過。在本文中,我們將準確介紹它們是什么,以及它們與更傳統(tǒng)的關(guān)系數(shù)據(jù)庫管理系統(tǒng) (RDBMS) 的比較,后者一直是過去 40 多年的主要軟件應(yīng)用程序。

受 Neo4j 用作圖查詢指導性介紹的一個小電影數(shù)據(jù)集的啟發(fā),我們將查看并排示例以及數(shù)據(jù)模型或查詢在圖數(shù)據(jù)庫和關(guān)系型數(shù)據(jù)庫。

在本文中,我們將:

  • 介紹圖數(shù)據(jù)庫,簡要介紹現(xiàn)有的兩種模型
  • 從概念上了解關(guān)系范式和圖范式之間的差異
  • 查看電影數(shù)據(jù)集,從 GDB 和 RDBMS 的角度比較和對比數(shù)據(jù)模型
  • 基于 Cypher(用于 GDB)或 SQL 比較和對比一些查詢
  • 討論電影示例中出現(xiàn)的更有趣的查詢,并準確地找出正在發(fā)生的事情

如果您想在閱讀文章之前(或在閱讀期間?。┦褂檬纠菥氹娪皵?shù)據(jù)集,我們非常歡迎您這樣做。

什么是圖數(shù)據(jù)庫?

首先,在我們深入研究什么是圖數(shù)據(jù)庫之前,讓我們定義這個術(shù)語。圖數(shù)據(jù)庫是一種“不僅僅是 SQL”(NoSQL,全程為Not Only SQL)的數(shù)據(jù)存儲。它們旨在以圖結(jié)構(gòu)存儲和檢索數(shù)據(jù)。

使用的存儲機制可能因數(shù)據(jù)庫而異。一些 GDB 可能使用更傳統(tǒng)的數(shù)據(jù)庫結(jié)構(gòu),例如基于表,然后在頂部有一個圖形 API 層。其他的將是“原生”GDB——從存儲、管理和查詢到數(shù)據(jù)庫的整個構(gòu)造維護數(shù)據(jù)的圖形結(jié)構(gòu)。許多當前可用的圖形數(shù)據(jù)庫通過將實體之間的關(guān)系視為“一等公民”來做到這一點。

不同類型的圖數(shù)據(jù)庫

廣義上有兩種類型的 GDB,資源描述框架 (RDF)/三重存儲/語義圖數(shù)據(jù)庫和屬性圖數(shù)據(jù)庫。

RDF GDB 使用三元組的概念,它是由三個元素組成的語句:主語-謂語-賓語。

主語將是圖中的資源或節(jié)點,對象將是另一個節(jié)點或文字值,謂詞表示主題和對象之間的關(guān)系。節(jié)點或關(guān)系上沒有內(nèi)部結(jié)構(gòu),一切都由唯一標識符以 URI 的形式標識。

這種結(jié)構(gòu)背后的動機是交換和發(fā)布數(shù)據(jù)。

 GDB 屬性專注于存儲接近邏輯模型的數(shù)據(jù)的概念。這反過來將基于數(shù)據(jù)本身所尋求的問題,并專注于使該表示盡可能高效地存儲和查詢。

與基于 RDF 的圖不同,節(jié)點和關(guān)系上有內(nèi)部結(jié)構(gòu),可提供豐富的數(shù)據(jù)表示以及相關(guān)的元數(shù)據(jù)。

屬性圖數(shù)據(jù)庫剖析

在本文的其余部分,我們將重點關(guān)注原生屬性圖數(shù)據(jù)庫,特別是 Neo4j。讓我們檢查一下主要組件。

屬性圖數(shù)據(jù)庫的主要組成部分如下:

  • 節(jié)點:在圖論中也稱為頂點——構(gòu)建圖的主要數(shù)據(jù)元素
  • 關(guān)系:在圖論中也稱為邊——兩個節(jié)點之間的鏈接。它將有方向和類型。沒有關(guān)系的節(jié)點是允許的,沒有兩個節(jié)點的關(guān)系是不允許的
節(jié)點和關(guān)系
  • 標簽:定義一個節(jié)點類別,一個節(jié)點可以有多個
  • 屬性:豐富一個節(jié)點或關(guān)系,不可以為空值!
標簽、類型和屬性

圖數(shù)據(jù)庫與關(guān)系數(shù)據(jù)庫

關(guān)系數(shù)據(jù)庫回顧

許多開發(fā)人員都熟悉傳統(tǒng)的關(guān)系數(shù)據(jù)庫,其中數(shù)據(jù)存儲在定義良好的模式中的表中。

表中的每一行都是一個離散的數(shù)據(jù)實體。行中的這些元素之一通常用于定義其唯一性:主鍵。它可能是一個唯一的 ID,也可能是一個人的身份證號碼之類的東西。

然后我們通過一個稱為規(guī)范化的過程來減少數(shù)據(jù)重復。在規(guī)范化中,我們將引用(例如某人的地址)移動到另一個表中。因此,我們從代表實體的行到代表該人地址的行獲得了一個引用。

例如,如果某人更改了他們的地址,您不希望該人的地址到處都是多個版本,并且必須嘗試記住該人地址所在位置的所有不同實例。規(guī)范化確保您擁有一個版本的數(shù)據(jù),因此您可以在一個地方進行更新。

然后當我們查詢時,我們要重構(gòu)這個歸一化的數(shù)據(jù)。我們執(zhí)行所謂的 JOIN 操作。

在我們的主實體行中,我們有一個主鍵,用于標識實體的 ID,比如說人。我們還有一個叫做外鍵的東西,它代表我們地址表中的一行。我們通過主鍵和外鍵連接這兩個表,并使用它在地址表中查找地址。這稱為 JOIN,這些 JOIN 在查詢時和讀取時完成。

當我們在關(guān)系數(shù)據(jù)庫中執(zhí)行 JOIN 時,它是一個集合比較操作,我們在其中查看我們的兩組數(shù)據(jù)重疊的位置(在這種情況下,集合是人員表和地址表)。在高層次上,這就是傳統(tǒng)關(guān)系數(shù)據(jù)庫的工作方式。

原生圖數(shù)據(jù)庫的工作原理:連接和無索引鄰接

讓我們快速瀏覽一下原生圖形數(shù)據(jù)庫及其工作原理。

我們談到了關(guān)系數(shù)據(jù)庫中的離散實體是表中的一行。在原生圖形數(shù)據(jù)庫中,該行相當于一個節(jié)點。它仍然是一個離散的實體,所以我們?nèi)匀挥羞@個標準化的元素。

一個節(jié)點將是一個實體。如果我們有個人節(jié)點,我們將有一個人一個節(jié)點。我們會有一定程度的獨特性,比如說社會安全號碼。

然而,關(guān)鍵的區(qū)別在于,當我們將這個人節(jié)點連接到另一個離散實體(例如地址)時,我們會在這兩個點之間創(chuàng)建物理連接(也稱為關(guān)系)。

地址會有一個指針,說明連接到節(jié)點的關(guān)系的出站部分是什么?然后我們有另一個指向關(guān)系的入站部分的指針指向另一個節(jié)點。

因此,實際上,我們正在收集一組指針,這是這兩個實體之間物理連接的體現(xiàn)。這就是最大的不同。

在關(guān)系數(shù)據(jù)庫中,您將在讀取時使用連接重新構(gòu)建數(shù)據(jù),這意味著在查詢時,它會嘗試找出事物如何映射在一起。

在圖數(shù)據(jù)庫中,由于我們已經(jīng)知道這兩個元素是相連的,所以我們不需要在查詢時查找映射。我們所做的只是跟蹤與其他節(jié)點的存儲關(guān)系。

這就是我們所說的無索引鄰接。與其他數(shù)據(jù)庫系統(tǒng)相比,這種無索引鄰接的概念是理解原生圖形數(shù)據(jù)庫性能優(yōu)化的關(guān)鍵。

無索引鄰接意味著在局部圖遍歷期間,按照連接圖中節(jié)點的這些指針(關(guān)系),操作的性能不依賴于圖的整體大小。這取決于連接到您正在遍歷的節(jié)點的關(guān)系數(shù)量。

當我們說 JOIN 是一個集合操作(??交集)時,我們使用關(guān)系數(shù)據(jù)庫中的索引來查看這兩個集合的重疊位置。這意味著 JOIN 操作的性能隨著表變大而開始變慢。

在大 O 符號術(shù)語中,這類似于使用索引的對數(shù)增長——類似于 O(log n) 并且隨著查詢中的 JOIN 數(shù)量呈指數(shù)增長。

另一方面,圖中的遍歷關(guān)系更多的是基于我們實際遍歷的節(jié)點中的關(guān)系數(shù)量的線性增長,而不是圖的整體大小。

這是圖數(shù)據(jù)庫為我們提供無索引鄰接的基本查詢時間優(yōu)化。從性能的角度來看,當我們考慮原生圖形數(shù)據(jù)庫時,這確實是最重要的事情。

電影圖簡介

我們已經(jīng)談到了圖形和關(guān)系數(shù)據(jù)庫之間的理論差異。現(xiàn)在讓我們開始看一些并排比較。

電影圖由一個數(shù)據(jù)集組成,該數(shù)據(jù)集由演員、導演、制片人、作家、評論家和電影組成,以及有關(guān)它們?nèi)绾蜗嗷ミB接的信息。  

電影數(shù)據(jù)集包括:

  • 133個人節(jié)點/實體
  • 38個電影節(jié)點/實體
  • 上述實體之間的 253 個關(guān)系/連接,描述的連接例如:
  • 導演電影的人
  • 在電影中扮演的角色和扮演的角色
  • 寫電影的人
  • 制作電影的人
  • 評論過電影并給出評分和摘要的人
  • 跟隨另一個人的人

雖然它是一個相對較小的數(shù)據(jù)集,但它全面地描述了圖的力量。

比較數(shù)據(jù)模型

首先我們來看看我們各自數(shù)據(jù)庫的數(shù)據(jù)模型。與所有數(shù)據(jù)模型一樣,它們的外觀最終取決于您提出的問題類型。所以讓我們假設(shè)我們要問以下類型的問題:

  • 一個人演過哪些電影?
  • 一個人與哪些電影有關(guān)?
  • 一個人曾經(jīng)合作過的所有合作演員是誰?

基于這些,以下是相關(guān)的潛在數(shù)據(jù)模型:

電影圖的實體關(guān)系數(shù)據(jù)模型電影圖的屬性圖數(shù)據(jù)模型

你會立刻發(fā)現(xiàn)一些東西——那些 ID 不見了!因為一旦我們知道那里有連接,我們就將數(shù)據(jù)連接在一起,我們不再需要它們,或者那些映射表來讓我們知道不同的數(shù)據(jù)行如何連接在一起。

比較查詢

現(xiàn)在讓我們繼續(xù)比較一些查詢。從:PLAY movies示例中選取一些最初的查詢,讓我們看一下 Cypher 查詢的一些并排比較,以及等效的 SQL 查詢是什么樣的。

什么是 Cypher,我聽到你問?Cypher 是一種圖查詢語言,用于查詢 Neo4j 圖數(shù)據(jù)庫。還有一個OpenCypher版本,許多其他供應(yīng)商都在使用它。

隨著我們進行查詢,它應(yīng)該開始變得更加清晰,圖數(shù)據(jù)庫以及一種幫助探索關(guān)系的查詢語言是如何真正開始發(fā)揮作用的。讓我們開始尋找湯姆漢克斯吧!

如何找到湯姆漢克斯

MATCH (p:Person {name: "Tom Hanks"})
RETURN p
Cypher
SELECT * FROM person 
WHERE person.name = "Tom Hanks"
SQL

如何找到湯姆漢克斯的電影

MATCH (:Person {name: “Tom Hanks”})-->(m:Movie)
RETURN m.title
SELECT movie.title FROM movie
INNER JOIN movie_person ON movie.movie_id = person_movie.movie_id
INNER JOIN person ON person_movie.person_id = person.person_id
WHERE person.name = "Tom Hanks"

如何找到湯姆漢克斯導演的電影

MATCH (:Person {name: "Tom Hanks"})-[:DIRECTED]->(m:Movie)
RETURN m.title
SELECT movie.title FROM movie
INNER JOIN person_movie ON movie.movie_id = person_movie.movie_id
INNER JOIN person ON person_movie.person_id = person.person_id
INNER JOIN involvement ON person_movie.involve_id = involvement.involve_id
WHERE person.name = "Tom Hanks" AND involvement.title = "Director"

如何找到湯姆漢克斯的合作演員

MATCH (:Person {name: "Tom Hanks"})-->(:Movie)<-[:ACTED_IN]-(coActor:Person)
RETURN coActor.name
WITH tom_movies AS (
    SELECT movie.movie_id FROM movie
    INNER JOIN person_movie ON movie.movie_id = person_movie.movie_id
    INNER JOIN person ON person_movie.person_id = person.person_id
    WHERE person.name = "Tom Hanks")
SELECT person.name FROM person
INNER JOIN person_movie ON tom_movies = person_movie.movie_id
INNER JOIN person ON person_movie.person_id = person.person_id
INNER JOIN involvement ON person_movie.involve_id = involvement.involve_id
WHERE involvement.title = "Actor"

使用 Cypher 進行更多查詢

希望您能了解 Cypher 和 SQL 查詢之間的差異。也許您也很高興了解更多關(guān)于它們的信息!我們將在博客文章中進一步提供一些參考。

現(xiàn)在,讓我們看看您可以在:PLAY movies圖表示例中找到的其他一些 Cypher 查詢,并解釋發(fā)生了什么。

沒有典型的培根數(shù)問題,任何電影圖表都是不完整的,我們的電影圖表也不例外!

到目前為止,我們看到的例子每次都遍歷一個關(guān)系。我們可以輕松地利用這些“寫入時連接”來遍歷許多關(guān)系來回答有趣的問題。

所以,回到凱文培根的數(shù)字。以下查詢將從 Kevin Bacon 人物節(jié)點開始,然后從該起點出發(fā)最多 4 跳,以帶回所有連接的電影和人物。

MATCH (bacon:Person {name:"Kevin Bacon"})-[*1..4]-(hollywood)
RETURN DISTINCT hollywood

我們可以通過使用*1..4查詢模式的關(guān)系部分的語法來做到這一點:

  • * 表示一切
  • 1..4 表示范圍 - 1 表示距離 1 跳,4 表示最多 4 跳

我們可以在這個電影數(shù)據(jù)集上做的另一件事是兩個節(jié)點之間的最短路徑。

在這個例子中,讓我們找出Kevin Bacon 和 Meg Ryan 之間的最短路徑。您會發(fā)現(xiàn)我們*再次將語法用于關(guān)系模式——指示一切。

對您來說可能是新的東西是p=. 您已經(jīng)看到我們?nèi)绾问褂霉?jié)點的引用(例如bacon或meg在我們當前的查詢中),并且我們可以對關(guān)系執(zhí)行相同的操作。

我們還可以對整個路徑(即所有涉及的節(jié)點和關(guān)系)進行引用。我們?yōu)榇耸褂玫恼Z法是refName =,在本例中是p=。

我們還使用 Cypher 函數(shù)shortestPath()——這是一個簡單的最短路徑函數(shù),它將返回兩個指定節(jié)點之間的第一個最短路徑。請注意,可能還有另一條同樣短的路徑,但這個簡單的函數(shù)只會帶回遇到的第一個路徑。

對于那些對其他路徑相關(guān)功能感興趣的人,請查看 APOC 和 GDS 中可用的功能。

MATCH p=shortestPath(
(bacon:Person {name:"Kevin Bacon"})-[*]-(meg:Person {name:"Meg Ryan"}))
RETURN p

給大家一個警告:您可能會看到這一點,[*]并試圖在沒有shortestPath()函數(shù)或1..4范圍約束的情況下運行您的圖形。但這很可能會導致一些意想不到的事情。

在我們與 Kevin Bacon 和 Meg Ryan 的示例中,即使在這個非常小的數(shù)據(jù)集中只有 253 個關(guān)系,節(jié)點和關(guān)系之間的所有可能的路徑組合也很容易遇到 Bacon 和 Ryan 之間的數(shù)百萬條不同的路徑。

當使用*在你的關(guān)系作為查詢的一部分,非常謹慎使用!這個問題沒有提出最短路徑,因為當遇到比當前識別的最短路徑更長的潛在路徑時,它會立即被丟棄。

一個簡單的推薦查詢

這里有兩個查詢真正展示了圖形數(shù)據(jù)庫的強大功能,我們可以輕松地使用數(shù)據(jù)中的連接來提出一些建議。

在我們的第一個查詢中,我們正在為湯姆漢克斯尋找新的合作演員,以與他尚未合作的人合作。查詢通過以下方式執(zhí)行此操作:

  • 首先,找到他已經(jīng)合作過的所有合作演員
  • 然后,找出所有的co-actors的co-actors(簡稱co-co-actors)
  • 接下來,我們要排除那些已經(jīng)和湯姆合作過的合作演員,并確保合作演員不是湯姆本人
  • 最后,我們返回建議的合作演員姓名,我們將對其進行排序,但與他們合作的合作演員的數(shù)量 - 與該合作演員合作的合作演員越多,建議越好。
MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors),
(coActors)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(cocoActors)
WHERE NOT (tom)-[:ACTED_IN]->()<-[:ACTED_IN]-(cocoActors) 
    AND tom <> cocoActors
RETURN cocoActors.name AS Recommended, count(*) AS Strength 
    ORDER BY Strength DESC

太好了,所以我們找到了一些潛在的合作演員。在下一個查詢中,我們想推薦湯姆克魯斯作為湯姆漢克斯合作的潛在新合作演員。但是,誰來介紹這些湯姆斯呢?回到我們?nèi)サ碾娪皥D表。

在這個查詢中,我們:

  • 找到湯姆漢克斯的合作演員,然后找出哪些合作演員也和湯姆克魯斯合作過
  • 然后我們將返回合作演員以及他們與湯姆漢克斯和湯姆克魯斯共同出演的電影
MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors),
(coActors)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(cruise:Person {name:"Tom Cruise"})
RETURN tom, m, coActors, m2, cruise



SQL

1 人點贊