我最喜歡的一部電影《危情諜戰》中有這樣一句臺詞,「Things Happen for A Reason」,不知道爲何,直到今天我對這句臺詞的印象都很深入,也許是當時看到這句話的時候腦海裏靈光一現,或是引發共鳴纔會烙下這麼深入的印象吧。幾乎從那之後,不管是作事仍是進行思考,我都會天然而然的養成從底層邏輯逐步的來挖掘和分析事物的核心原理。html
好比說最近在作產品的技術架構設計,其中幾乎有一大半時間都是在梳理業務流程和需求,將業務進行分類處理,再作相關的技術調研。其中 Neo4j 算是技術方案中的核心角色之一,這篇 post 也算是作這個架構設計中關於 Neo4j 這個 NoSQL 數據庫的一些感想。java
如何快速的瞭解 Neo4j 呢,網上有不少很長的文字,不過我以爲都沒有簡單明確的講清楚。我想了想,如何讓你們最快最高效的理解 Neo4j,最好的辦法就是下面總結的幾句話:node
圖數據庫有不少,在這個 wiki 上有一個比較完整的列表:List of graph databases,其中有的彷佛已經沒有維護了,有的呢是傳統的 NoSQL 數據庫,可是提供圖數據結構存儲,有的也像 Neo4j 同樣是專用的圖數據庫,可是即便是專門的圖數據庫,其底層實現,應用場景和生態也是有天差地別的。mysql
今天咱們着重看看 Neo4j,我們主要從下面幾個方面來學習和深刻理解 Neo4j。redis
前面咱們講過,如今其實已經有不少圖數據庫了,其中有一些 document 類 NoSQL 數據庫(假設叫 A DB),也支持圖數據結構的存儲和檢索,那這類數據庫和 Neo4j 最大的區別,就是接下來咱們要說的 Native 和 non-Native 圖數據庫了。簡單來講,Neo4j 是 Native 的圖數據庫,A DB 屬於 non-Native 圖數據庫了。算法
所謂 Native 圖數據庫,中文通常翻譯爲 「原生圖數據庫」,指從一開始即是爲了解決圖類數據結構而設計的數據庫。對了解數據結構的同窗來看,這裏的 「原生」 的體現,其實主要就在兩個方面:sql
咱們也從這兩個點來聊聊 Neo4j。數據庫
圖數據結構的存儲編程
無論是什麼數據庫,mysql,sql server,mongo,neo4j 等等,其存儲的數據,最終都要落地到文件系統上的,那 neo4j 落地的文件大概都是什麼樣的呢?這裏我以本地的一個應用的數據庫來介紹一下。緩存
上面是個人本地一個圖數據庫的數據庫文件,能夠看出 Neo4j 把其數據庫文件分爲四大類來分類存儲:
其實按照咱們圖論中的通常說法,其實對於一個圖數據結構來講,只須要存儲節點和關係就能夠了,以下面這個圖數據同樣:
可是現實生活中,咱們的圖結構是很是複雜的,並且數據量很大,常常須要作各類各樣的分析,甚至還須要作聚類分析(這裏的聚類分析不是指機器學習裏面的聚類模型,而是指把同一類數據提取出來單獨作分析,相似於 groupby 的操做)。因此 Neo4j 裏面在文件系統上單獨存儲了標籤和屬性,就是爲了在作檢索和分析的時候保證性能。下面我們以一個形象的例子來介紹什麼是標籤,什麼是屬性。
這裏是咱們應用中一個實際案例,你們看看咱們對標籤和屬性的劃分:
-- 這裏我們先看看這個語句表達的什麼意思,這是 Neo4j 專用的查詢語言 Cypher
-- 你們不要聽到又要學一門新語言就打退堂鼓哈,哈哈哈,後面咱們會簡單講講這個語言的,兩個字:簡單
-- 下面這個查詢語句含義以下:
-- step 1: 首先查詢一個具備標籤爲 「滬深股市」,且有一個屬性對 「{name: "sha_600610"}」 的節點,
-- step 2: 而後查找這個節點的全部雙向關係
-- step 3: 在這些關係的中,要求另一個節點具備 「人物_公司股東」 這個標籤
-- step 4: 返回這個節點,以及知足要求的關係和相關節點
MATCH (node1:滬深股市 {name: "sha_600610"})-[relation]-(node2:人物_公司股東)
RETURN node1, node2, relation
複製代碼
有人可能會問,其實也能夠不用單獨存儲標籤和屬性的呀,咱們徹底能夠把標籤和屬性做爲節點的元信息存儲在節點裏面。這個說法首先在理論和工程實現上來講徹底沒有問題。可是試想一下,一個節點和關係很是很是大的圖,要作上面這個檢索的時候,會不會面臨一些問題?而 Neo4j 的這種存儲設計,是如何來解決這些問題的呢?這兩個問題留給你們思考,能夠在評論裏交流你們的想法。
圖數據的處理和查詢
圖數據的處理,同傳統數據庫同樣的四字法則:CURD。而由於圖數據的特殊性,在大多數狀況下,數據庫中每個節點都有與之相連的關係,每一條關係,都必須有這個關係對接的兩個節點。這就要求圖數據庫的建立,更新,讀取,刪除都必須知足一致性(或者事務完整)的原則。Neo4j 如何實現 ACID 的底層算法我尚未看,感興趣的能夠先看看這個 talk: Evolution of Neo4j with ACID transactions, HA cluster, and CRUD transactions。
數據庫的查詢,這個無論是什麼數據庫,都是最基礎的功能,對於 sql 和 nosql 來講,簡單的查詢其實並無多大差異,只是一些複雜查詢或者針對特定場景的查詢條件下才會特意的選擇某一種數據庫。好比最多見的就是如今的大多數 WEB 應用,在存儲上基本上都會涉及下面幾類數據庫:
可是在實際應用中,會常常性出現一些很複雜的查詢語句,好比下面幾種例子:
在 APP 應用中,咱們想要知道昨天新增的用戶中,有多少今天是繼續登錄 APP 的,這種查詢擴展開來,就是作 APP 用戶增加中耳熟能詳的 cohort analysis,下面有截圖展現這種分析的圖表
人際關係網絡中,給定兩個節點,查詢這兩個節點的最短路徑,而且這些路徑知足必定的條件?這樣說是否是有點書面化,那咱們換一種說法,在社交網絡中,若是你想認識特朗普的女兒伊萬卡,查詢一條最短路徑讓你結識到伊萬卡,而且這個路徑中每一個人都是單身的。相信我,這是完徹底全可能的,並且無論你是誰,極可能經過6~7我的就能認識伊萬卡了,amazing。不信的同窗能夠去了解了解六度分隔理論這個東西。
在上面這些複雜查詢中,特別是第二種狀況,用傳統的關係型數據庫實現起來實在是不切實際。不光是性能上的緣由,還有開發人員可用性的考慮,試想若是這樣子一個查詢,用 sql 來寫的話,這個 sql 語句應該怎樣寫?下面是一個案例,展現一個查詢用 neo4j 和 sql 下的對比。
上面咱們講了 Neo4j 的數據存儲大概分爲:節點,關係,屬性,標籤這四個類別,這是 Neo4j 存儲模塊的工程實現,而如今咱們要講的 Property Graph Model 就是這個工程實現的理論來源。正若有的朋友會說,其實簡單的存儲節點和關係,而後其餘標籤,屬性什麼的均可以算作節點和關係的元信息來存儲就好了。工程上實現的確也能夠,可是理論上去研究,會發現這樣的效率會很低。
Property Graph Model,簡單的說,其實就是 Neo4j 的底層數據模型,這個數據模型推進了工程上按照四個方面來實現存儲模型。那什麼叫 Property Graph Model 呢,其實從核心上來說,Property Graph Model 有兩個核心要素:
下面是一個 Property Graph Model 的展現:
每種數據庫都有本身的一套查詢語言或者標準,就算是 SQL 中的王者 Sql Server 和 MySQL,其在一些語法細節上也有差別,更別說 Mongo, Redis 相似的非關係型數據庫了。Neo4j 也同樣,有本身專屬的查詢語言 Cypher。有的朋友聽到若是用 Neo4j 又要學習一門新的編程語言就很頭疼,其實這裏能夠分享下我本身的經歷,之前我剛接觸 Neo4j 的時候,確實以爲又要學習一門新語言有些蛋疼,但是後來接觸下來,我以爲可能普通人估計只用花一兩天就能掌握 Cypher 了,由於 Cypher 的語意確實簡潔,直觀。
好比下面咱們直接經過代碼來對比下 MySQL 和 Neo4j 裏面的查詢,我相信即便不寫任何註釋,即便第一次接觸 Neo4j 的人也能輕輕鬆鬆的看懂這些查詢語句。
<!-- 1. 全表掃描 -->
<!-- mysql -->
SELECT p.*
FROM products as p;
<!-- neo4j -->
MATCH (p:Product)
RETURN p;
<!-- 2. 查詢價格最貴的10個商品,只返回商品名字和單價 -->
<!-- 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;
<!-- 其餘的寫法 -->
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. 模糊查詢和數值過濾 -->
<!-- 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. 多表聯合查詢-->
<!-- 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
複製代碼
隨着應用的複雜化,技術的專業化,如今愈來愈難全面準確的評論一個框架,平臺甚至產品的好壞了。不過在我自身的經驗裏,我以爲一個好的框架,確定是有一個完善的周邊生態的。這也是我常常說,在比較常見的開源框架裏面,neo4j 和 spark 是我以爲作得比較出色的,同時也是我最看好的兩個開源團隊。其一是他們作的不少事情都是從用戶角度來考慮的,而不只僅是單純從工程角度來思考,特別是 spark 團隊,還成立了他們本身的平臺 databricks 提供給大衆使用;其二是這兩個開源產品的周邊生態都很豐富,體如今兩個方面,和流行框架產品的融合度,以及本身周邊開發工具的豐富程度。
好比說 Neo4j,官網有專門介紹其生態系統的地方,感興趣的盆友們請移步這裏:Neo4j Ecosystem。在這麼多開發工具裏面,最讓我驚歎的是 Neo4j browser 和今年上半年發佈的 neo4j bloom。
neo4j browser
neo4j browser 有點相似於 mysql 裏面的 workbench 或者 mongo 裏面的 RoboMongo,說白了就是一個數據庫的客戶端,可是 neo4j browser 作得很是友好,甚至看上去,用起來就像是一個成熟的產品同樣。記得好久好久之前我在跟團隊分享 neo4j 的時候,團隊裏面對 neo4j 不熟悉的人還問我這個哪一個產品,交互作得很不錯。amazing!
neo4j bloom
提及 neo4j bloom,到如今我還忍不住想稱讚 neo4j 團隊,雖然 bloom 不是實現了什麼特別 NB 的算法,也沒有什麼偉大的創新,可是這個產品倒是實打實的站在用戶層面去考慮,一會兒就把 neo4j 的可用性,宜用性,實用性提升了好幾個檔次了。
談起 bloom,還得說咱們準備作的知識圖譜產品開展開了,咱們作的每個圖譜,都有兩個亮點的地方:
其中第一個亮點,咱們以前作了不少調研,發現不少傳統的圖譜產品在內容更新和修改上都作得很複雜很很差用,好比說有的圖譜更新是後臺提供一個 excel 表格給相關專業人員去填寫這個表格;有的產品呢是有專門作的一個內容管理後臺,學會用那個管理後臺還得話很多時間。最重要的是,若是以這種模式提供給專家去修改內容,首先咱們還得讓專家理解從 excel模版或者內容管理後臺 到圖譜內容結構這樣一個過程的思惟轉變,要否則極有可能出現內容修改錯誤的狀況。
因此,咱們一直想作一個容許特定帳號直接在圖譜上進行修改的產品,好比說給專家的帳號開通修改權限,專家能夠和普通用戶打開一樣的產品頁面,看到一樣的產品內容,只是專家帳號在點擊圖譜節點時,能夠有一個修改節點及其關係的權限,作到實時修改,這樣子極大的提升了專家操做的效率,並且還節省了很多前臺,後臺開發的工程量。
正當咱們定下這個方案不久,我就看到了 neo4j 發佈的關於 bloom 的文章和視頻,真的大爲驚歎,當時我就直接把文章和視頻轉發到團隊的微信羣裏,neo4j 開發的 bloom 和咱們設想的產品模式幾乎一致,它直接想把咱們作的事完成了,並且還作得漂亮,光說 bloom 的那個交互,就已經把國內很多作圖譜的產品甩開幾條街了。
感興趣的盆友能夠先看看這個視頻體驗體驗:Neo4j Bloom: Investigating Patterns in Financial Transactions
伴隨 2012 年 google 正式發佈知識圖譜搜索引擎和 2013 年 facebook 開放知識圖譜搜索入口以來,知識圖譜迎來了一波發展浪潮,neo4j 做爲原生的圖數據庫,也迎來了它的春天。可是就業內朋友交流來看,知識圖譜類創業公司和產品數量上並很少,質量上也缺少重磅產品,除了傳統的社交網絡方面(這方面有 facebook 的關係搜索和 linkedin 的人脈搜索)。究其緣由,其實有兩個比較大的緣由,一個是熟悉圖數據庫,瞭解知識圖譜的人才還比較少;另一個緣由是業務的抽象化自己門檻就比較高。
葛優在《天下無賊》中有一句話特別出名 「21世紀什麼最貴?人才「。咱們在業內交流下來,其實也發現一個比較值得思考的問題,就是雖然知識圖譜發展也有六七年的樣子,但其實真正在圖數據庫方面去研究的人才不多。一樣是 NoSQL 數據庫,爲何常常能夠聽到關於 mongo,redis 等數據庫技術的討論,卻不多見到 neo4j 的技術問答呢?咱們以爲,其中的緣由多是由於 mongo,redis 這類數據庫相對於 neo4j 來講,是比較 general 的技術,就是說 mongo,redis 在各類應用中的大多數場景中均可以用到,它們並不依賴某一種特定類型的應用。而 neo4j 則有所不一樣,neo4j 比較專一在圖相關的數據結構的存儲和查詢方面,並且 neo4j 自己還須要使用特定的查詢語言 (cypher,咱們前面也有講過),一些企業和工程師就缺少動力去使用這門新的技術。因此從這兩個點來講,neo4j 的辨識度,流行度遠遠不及 mongo,redis 類的 nosql。下面是我查詢了 google trend 自 2008 年來這三個關鍵詞的表現,可見一斑。
不過若是隻看 neo4j 的 google trend 數據的話,它仍是慢慢在走向人們的視野。
而說到知識圖譜,其實國內外在這方面的成熟產品能夠說百裏挑一,出去上面咱們說的技術方面的人才儲備問題外,其實最大的仍是自己業務的抽象化門檻過高。好比說,在金融領域有很多創業公司嘗試過用知識圖譜來描述和分析一個公司,我們來看看下面這個案例截圖:
上面這個金融知識圖譜,眨眼一看,彷佛信息還挺齊全,但是若是站在一個金融專業投資者角度來看,問題就暴露不少了:
還有其餘不少的點就不一一列出了,列這麼多其實只是想說,在知識圖譜這個領域,無論作什麼產品都要於業務自己緊密相連,如今是一個論專業化,比專業性的時代,不能簡簡單單的把一個技術套到一個產品上就叫創新了,而是要經過技術把以前難以實現的具備價值的事情作成作好。
若是要用幾句話來總結這篇 post 裏我想表達的意思,我想應該是下面幾句: