目錄
導(dǎo)語(yǔ)
圖數(shù)據(jù)庫(kù)該如何操作和查詢呢?我們知道關(guān)系型數(shù)據(jù)庫(kù)用的是SQL( Query ),它也是數(shù)據(jù)庫(kù)領(lǐng)域第一個(gè)國(guó)際標(biāo)準(zhǔn),在大數(shù)據(jù)庫(kù)和NoSQL類(lèi)型數(shù)據(jù)庫(kù)廣泛發(fā)展之前的唯一的一個(gè)數(shù)據(jù)庫(kù)查詢語(yǔ)言國(guó)際標(biāo)準(zhǔn)。
本文中,我們會(huì)介紹圖查詢語(yǔ)言GQL(Graph Query )的基礎(chǔ)概念,以及GQL與SQL之間的差異。
01、用進(jìn)化的視角看數(shù)據(jù)庫(kù)和查詢語(yǔ)言的演進(jìn)
如果讀者對(duì)SQL語(yǔ)言的演進(jìn)有所了解的話,就知道是它直接推動(dòng)了關(guān)系型數(shù)據(jù)庫(kù)的發(fā)展。此外,互聯(lián)網(wǎng)的崛起還催生了NoSQL的誕生和崛起,其中重要的一個(gè)因素是關(guān)系型數(shù)據(jù)庫(kù)無(wú)法很好地應(yīng)對(duì)數(shù)據(jù)處理速度、數(shù)據(jù)建模靈活性的訴求。
NoSQL數(shù)據(jù)庫(kù)一般被分為以下5類(lèi),每一類(lèi)都有其各自的特性:
圖:數(shù)據(jù)庫(kù)查詢語(yǔ)言的進(jìn)化(一家之言)
SQL的出現(xiàn)已將近半個(gè)世紀(jì)了,并且迭代了很多版本(平均每3至4年就一次大迭代),其中最知名的非SQL的92/99莫屬,例如92版本中在FROM語(yǔ)句中增加了子查詢功能,在99 年版本中增加了CTE功能等,這些都極大地增加了關(guān)系型數(shù)據(jù)庫(kù)的靈活性。然而,關(guān)系型數(shù)據(jù)庫(kù)始終存在一個(gè)“弱點(diǎn)”,那就是對(duì)于遞歸型數(shù)據(jù)結(jié)構(gòu)的支持。
所謂遞歸數(shù)據(jù)結(jié)構(gòu),指的是有向關(guān)系圖的功能實(shí)現(xiàn)。令人感到諷刺的是,關(guān)系型數(shù)據(jù)庫(kù)的名字雖然包含了關(guān)系,但它在設(shè)計(jì)伊始就很難支持關(guān)聯(lián)關(guān)系的查詢。為了實(shí)現(xiàn)關(guān)聯(lián)查詢,關(guān)系型數(shù)據(jù)庫(kù)不得不依賴表連接操作,而每一次表連接都意味著潛在的表掃描操作,以及隨之而來(lái)的性能上的指數(shù)級(jí)下降和SQL語(yǔ)句、代碼復(fù)雜度的直線上升。相信這也是很多程序員的夢(mèng)魘吧。
表連接操作的性能損耗是直接源自于關(guān)系型數(shù)據(jù)庫(kù)的基礎(chǔ)設(shè)計(jì)思想:
如果我們看一下NoSQL中的核心理念,在數(shù)據(jù)建模中突出了數(shù)據(jù)去正則化。
所謂數(shù)據(jù)正則化,指的是用空間換取時(shí)間(犧牲空間來(lái)?yè)Q取更高的性能!)在NoSQL(也包括等,例如典型的3、5、7份拷貝的理念)中,數(shù)據(jù)經(jīng)常被以多份拷貝的方式存儲(chǔ) ,而這樣做的好處在于數(shù)據(jù)可以以近鄰計(jì)算資源的方式被處理。這種理念和SQL中的只存一份正則化設(shè)計(jì)思路是截然相反的。后者或許可以節(jié)省一些存儲(chǔ)空間,但是對(duì)于復(fù)雜的SQL操作而言,帶來(lái)的是性能的損耗。
預(yù)先定義數(shù)據(jù)的表模式的理念是SQL與NoSQL的另一大差異。對(duì)于初次接觸這一概念的讀者而言,理解這個(gè)點(diǎn)會(huì)有些復(fù)雜,從下面這個(gè)角度去理解或許更直觀:
在關(guān)系型數(shù)據(jù)庫(kù)中,系統(tǒng)管理員(DBA),需要先定義表的結(jié)構(gòu)(),然后才會(huì)加載第一行數(shù)據(jù)進(jìn)入數(shù)據(jù)庫(kù),他不可能動(dòng)態(tài)的更改表的結(jié)構(gòu)。這種僵化性對(duì)于固定模式、一成不變的數(shù)據(jù)結(jié)構(gòu)和業(yè)務(wù)需求而言或許不是什么大問(wèn)題。但是,讓我們想象一下,如果數(shù)據(jù)模式可以自我調(diào)整,并能根據(jù)流入的數(shù)據(jù)動(dòng)態(tài)調(diào)整,這就給了我們極大的靈活性。
對(duì)于強(qiáng)SQL背景的人而言,這是很難被想象的。但是sql 數(shù)據(jù)庫(kù)關(guān)系圖,如果我們暫時(shí)拋棄掉我們僵化的、限制性的思維,取而代之以一種成長(zhǎng)性的思維方式 ——我們所要達(dá)成的目標(biāo)是一種“-Free或”的數(shù)據(jù)模式,也就是無(wú)需預(yù)先設(shè)定數(shù)據(jù)模式,數(shù)據(jù)之間的關(guān)聯(lián)性不需要預(yù)先定義和了解,隨著數(shù)據(jù)的流入,它們會(huì)自然地形成某種關(guān)聯(lián)關(guān)系 ——而數(shù)據(jù)庫(kù)所需要做的是對(duì)應(yīng)著這些數(shù)據(jù)“因地制宜”地來(lái)處理如何查詢與計(jì)算。
在過(guò)去幾十年中,數(shù)據(jù)庫(kù)程序員已經(jīng)被訓(xùn)練的一定要先了解數(shù)據(jù)模型,不論它是關(guān)系型表結(jié)構(gòu)還是實(shí)體E-R模式圖。了解數(shù)據(jù)模型當(dāng)然有它的優(yōu)點(diǎn),然而,這也讓開(kāi)發(fā)流程變得更加復(fù)雜和緩慢。如果讀者們還記得上一次你參與的交鑰匙解決方案的開(kāi)發(fā)周期有多長(zhǎng)?一個(gè)季度、半年、一年還是更久?在一個(gè)有8000張表的數(shù)據(jù)庫(kù)中,沒(méi)有任何一個(gè)DBA可以完全掌握所有表之間的關(guān)聯(lián)關(guān)系。這個(gè)時(shí)候,我們更愿意把這套脆弱的系統(tǒng)比作一個(gè)定時(shí)炸彈,而你的所有業(yè)務(wù)都綁定在其上!關(guān)于無(wú)模式(-Free),筆者并不想解釋文檔型數(shù)據(jù)庫(kù)或者寬列據(jù)庫(kù),盡管它們都多少有一些和圖數(shù)據(jù)庫(kù)相似的設(shè)計(jì)理念。在下文中會(huì)用一些具體的圖數(shù)據(jù)庫(kù)上的例子來(lái)幫助讀者理解無(wú)模式意味著什么。
02、圖語(yǔ)言(GQL)的大道至簡(jiǎn)之法
在圖數(shù)據(jù)庫(kù)中,邏輯上只有兩類(lèi)基礎(chǔ)的數(shù)據(jù)類(lèi)型:
一個(gè)頂點(diǎn)具有它自己的ID和屬性(標(biāo)簽、類(lèi)別及其它屬性)。
邊也類(lèi)似,除了它通常是由兩個(gè)頂點(diǎn)的順序決定的(所謂有向圖的概念指的是每條邊由一個(gè)初始頂點(diǎn)對(duì)應(yīng)一個(gè)終止頂點(diǎn),再加上其它屬性所構(gòu)成,例如邊的方向、標(biāo)簽、權(quán)重等)。
此外,圖數(shù)據(jù)庫(kù)并不需要任何預(yù)先定義的模式或表結(jié)構(gòu)。這種極度簡(jiǎn)化的理念恰恰和人類(lèi)如何思考以及存儲(chǔ)信息有著很大的相似性 —— 我們通常并不在腦海中設(shè)定表結(jié)構(gòu),我們是隨機(jī)應(yīng)變的!
現(xiàn)在,讓我們看一看一些真實(shí)世界場(chǎng)景中的圖數(shù)據(jù)庫(kù)實(shí)現(xiàn),例如下圖中的一個(gè)典型的圖數(shù)據(jù)集中的頂點(diǎn)的屬性定義,它包含了最初的幾個(gè)字段的定義,例如desc, level, name, type,但是也存在一些動(dòng)態(tài)生成、擴(kuò)增的字段,例如#cc, #pr, #等等。如果比照著關(guān)系型數(shù)據(jù)庫(kù)而言,整個(gè)表的結(jié)構(gòu)是動(dòng)態(tài)可調(diào)整的。
圖:圖數(shù)據(jù)集中的頂點(diǎn)屬性(動(dòng)態(tài)屬性)
注意上圖中的name和type字段的屬性為類(lèi)型,它可以最大化兼容廣譜的數(shù)據(jù)類(lèi)型,進(jìn)而提供最大化的靈活性。頂點(diǎn)之間如何產(chǎn)生關(guān)聯(lián)也無(wú)需被預(yù)先定義,這樣所形成的關(guān)聯(lián)網(wǎng)絡(luò)也是靈活的。
細(xì)心的讀者一定會(huì)問(wèn)到,這種靈活性怎么來(lái)實(shí)現(xiàn)和保證性能優(yōu)化呢?常識(shí)的做法是通過(guò)存儲(chǔ)與計(jì)算分層來(lái)實(shí)現(xiàn),例如為了實(shí)現(xiàn)極佳的計(jì)算性能,數(shù)據(jù)可以動(dòng)態(tài)地加載進(jìn)入內(nèi)存計(jì)算(LTE vs. UFE = Load-to- vs. -from-),當(dāng)然內(nèi)存計(jì)算只是一部分,支持并發(fā)計(jì)算的數(shù)據(jù)結(jié)構(gòu)也是必不可少的。
鍵值庫(kù)可以被看作是前-SQL(Pre-SQL在這里表達(dá)的是一種相對(duì)于SQL而言更原始的特型)的非關(guān)系型架構(gòu)庫(kù),圖數(shù)據(jù)庫(kù)則可被看作是后SQL(Post-SQL在這里表達(dá)了一種相對(duì)而言的先進(jìn)性)時(shí)代的,真正意義上支持遞歸式數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)庫(kù)。
今天的不少NoSQL數(shù)據(jù)庫(kù)都試圖通過(guò)兼容SQL來(lái)獲得認(rèn)可,但是在筆者看來(lái),SQL的設(shè)計(jì)理念是極度表限定的,所謂“表限定”(table-),指的是它的整個(gè)理念都是限定在二維世界中的,當(dāng)要進(jìn)行表連接操作時(shí),就好比要去進(jìn)入到三維或更高維的空間進(jìn)行操作,而這也是非常低效和反直覺(jué)的,這是基于關(guān)系型數(shù)據(jù)模式—SQL本身的低維屬性決定的。
圖數(shù)據(jù)庫(kù)天然是高維的,除非它的實(shí)現(xiàn)是基于關(guān)系型數(shù)據(jù)庫(kù)或列數(shù)據(jù)庫(kù)實(shí)現(xiàn)的,那么本質(zhì)上這種非原生圖的設(shè)計(jì)依然是低維驅(qū)動(dòng)的,它的效率又怎么可能會(huì)高呢?!
在圖上面的操作天然是屬于遞歸式的,例如廣度優(yōu)先搜索或深度優(yōu)先搜索。當(dāng)然,僅僅從語(yǔ)言的兼容性而言,圖上面一樣也可以支持SQL類(lèi)操作來(lái)保持向關(guān)系型用戶群的習(xí)慣兼容,就像Spark SQL或CQL一樣,無(wú)論它的意義到底有多大或多長(zhǎng)久。
03、 GQL (嬴圖)圖語(yǔ)言
下面,讓我們來(lái)看一些通過(guò)用Ultpa GQL( Graph Query , 圖數(shù)據(jù)庫(kù)查詢語(yǔ)言)實(shí)現(xiàn)的圖查詢功能實(shí)例。同時(shí),請(qǐng)仔細(xì)思考用SQL 或是其它NoSQL 數(shù)據(jù)庫(kù)將如何才能完成同樣的任務(wù)?
任務(wù)1:從某個(gè)頂點(diǎn)出發(fā),找到它的第1到第K層(跳)的所有鄰居并返回。
Ultpa GQL 是與 Graph 高并發(fā)實(shí)時(shí)圖數(shù)據(jù)庫(kù)匹配的查詢語(yǔ)言,又名嬴圖查詢語(yǔ)言。除了明顯的性能優(yōu)勢(shì)外,Ultpa GQL的另外一個(gè)重要特點(diǎn)是高易用性,容易掌握,并有貼近自然語(yǔ)言的易讀性。可以通過(guò) 、 CLI或 SDK/API的接口調(diào)用,只需要1行 GQL即可實(shí)現(xiàn)上面的查詢。
圖:通過(guò)()操作對(duì)聯(lián)通子圖進(jìn)行遍歷
spread().src(123).depth(6).spread_type(“BFS”).limit(4000);
上面的語(yǔ)句簡(jiǎn)單易懂,基本上不需要太多解釋?zhuān){(diào)用()函數(shù),從頂點(diǎn)123出發(fā),搜索深度為6層,以BFS的方式進(jìn)行搜索,限定返回最多4000個(gè)頂點(diǎn)(以及關(guān)聯(lián)的邊)。
在上圖中,紅色的小點(diǎn)就是起始頂點(diǎn),通過(guò)以上語(yǔ)句操作的全部返回的頂點(diǎn)和邊所形成的子圖直接顯示在 Graph的WEB界面上了。
事實(shí)上,()這個(gè)操作相當(dāng)于允許從任何頂點(diǎn)出發(fā)找到它的聯(lián)通子圖——或者說(shuō)它的鄰居網(wǎng)絡(luò)的形態(tài)可以被直接計(jì)算出來(lái),并通過(guò)可視化界面直觀地展示出來(lái)。用這種方式也可以看出生成的聯(lián)通子圖中的頂點(diǎn)和邊所構(gòu)成的熱點(diǎn)、聚集區(qū)域等圖上的空間特征,而并不需要傳統(tǒng)數(shù)據(jù)庫(kù)中的 E-R模型圖。
任務(wù)2:給定的多個(gè)頂點(diǎn),自動(dòng)組網(wǎng)(形成一張頂點(diǎn)間相互聯(lián)通的網(wǎng)絡(luò))。
本查詢相對(duì)于習(xí)慣使用傳統(tǒng)數(shù)據(jù)庫(kù)的讀者來(lái)說(shuō)或許就顯得過(guò)于復(fù)雜了,用SQL也許無(wú)法實(shí)現(xiàn)這個(gè)組網(wǎng)功能。
但是,對(duì)于人的大腦而言,這是個(gè)很天然的訴求——當(dāng)你想在張三、李四、王五和趙六之間組成一張關(guān)聯(lián)關(guān)系的網(wǎng)絡(luò)的時(shí),你已經(jīng)開(kāi)始在腦海里繪制下面這張圖了(見(jiàn)下圖)。
圖:在 中自動(dòng)生成的網(wǎng)絡(luò)(子圖)
很顯然, GQL傾向于繼續(xù)使用1行代碼來(lái)實(shí)現(xiàn)這個(gè)“不可能”的操作:
autoNet().addSrcs(12,21,30,40,123).depth(4).limit(5)
( )就是我們調(diào)用的主要函數(shù),它的名字已經(jīng)非常直白了——自組網(wǎng)操作。你只需要提供一組頂點(diǎn)的ID信息,組網(wǎng)搜索的深度(4層=4跳),以及任意兩個(gè)頂點(diǎn)間的路徑數(shù)量限制(5)。下面,我們來(lái)從純數(shù)學(xué)的角度來(lái)分析一下這個(gè)組網(wǎng)操作的計(jì)算復(fù)雜度:
注:
假設(shè)圖中的(邊數(shù)/頂點(diǎn)數(shù))比例為4(平均值),也就是E/V=4,搜索深度為4的時(shí)候每條路徑需
要平均計(jì)算256(44)次。
這個(gè)查詢?cè)诂F(xiàn)實(shí)世界應(yīng)用中的意義非同凡響。
例如執(zhí)法機(jī)關(guān)會(huì)根據(jù)電話公司的通話記錄來(lái)跟蹤多名嫌疑人的通話所組成的深度網(wǎng)絡(luò)的特征來(lái)判斷是否有其它嫌疑人關(guān)聯(lián)其中,犯罪集團(tuán)是否存在某種異動(dòng),或者任意個(gè)數(shù)的嫌疑人構(gòu)成的犯罪組織間的微妙的聯(lián)動(dòng)關(guān)系等。
在傳統(tǒng)大數(shù)據(jù)技術(shù)框架上,這種多節(jié)點(diǎn)的組網(wǎng)操作極為復(fù)雜,甚至是沒(méi)有可能完成的任務(wù)。
原因是因?yàn)橛?jì)算復(fù)雜度太高 ,對(duì)于計(jì)算資源的需求太大,在短時(shí)間內(nèi)沒(méi)有可能完成,或者是以T+7(亦或T+15、T+30)的方式實(shí)現(xiàn),等到結(jié)果出來(lái)的時(shí)候,嫌疑人早已逃之夭夭或者罪案已發(fā)生良久了。
假設(shè)有1000個(gè)嫌疑人需要參與組網(wǎng),他們之間形成的網(wǎng)絡(luò)的路徑至少有50萬(wàn)條(1,000 * 999 / 2)。如果查詢路徑深度為 6層,如上所述,這個(gè)計(jì)算復(fù)雜度是20億次(假設(shè)E/V=4,實(shí)際上E/V可能>=10,那么計(jì)算次數(shù)可能達(dá)到50萬(wàn)億次)。基于Spark架構(gòu)的計(jì)算平臺(tái)可能需要數(shù)天來(lái)完成運(yùn)算。
而利用 Graph,該操作是以實(shí)時(shí)到近實(shí)時(shí)(T+0)的方式完成的,我們?cè)诓煌臄?shù)據(jù)集上做過(guò)性能評(píng)測(cè),的性能至少是Spark框架的幾百倍到數(shù)千倍——Spark系統(tǒng)需要1天完成的計(jì)算, 僅需數(shù)秒、數(shù)分鐘!當(dāng)與罪犯斗爭(zhēng)的時(shí)候,每一秒都很寶貴。
圖:基于的實(shí)時(shí)大規(guī)模的組網(wǎng)操作
對(duì)于實(shí)時(shí)高并發(fā)圖數(shù)據(jù)庫(kù),性能肯定是“第一等公民”,但是這并沒(méi)有讓我們把語(yǔ)言的簡(jiǎn)潔、直觀、易懂性當(dāng)做次等公民。絕大多數(shù)人會(huì)發(fā)現(xiàn) GQL是如此的簡(jiǎn)單,掌握了最基本的語(yǔ)法規(guī)則后,通常閱讀操作手冊(cè)幾分鐘到30分鐘內(nèi),就可以開(kāi)始寫(xiě)出你自己的 GQL查詢語(yǔ)句了。
GQL 借鑒并采用了鎖鏈?zhǔn)讲樵儯╟hain-query)的語(yǔ)言風(fēng)格,對(duì)于熟悉文檔型數(shù)據(jù)庫(kù)的讀者而言,上手 GQL就更加簡(jiǎn)單了。例如,一個(gè)簡(jiǎn)單的鏈?zhǔn)铰窂剑c(diǎn)到點(diǎn))查詢語(yǔ)句,看下圖:
圖:鏈?zhǔn)綀D查詢語(yǔ)言之路徑查詢
這個(gè)例子查詢兩個(gè)頂點(diǎn)間深度為5度的路徑,限定返回5條路徑,并且返回匹配的屬性“name”(通常是頂點(diǎn)或邊的名稱(chēng)屬性)。
我們?cè)賮?lái)看一個(gè)稍微復(fù)雜一點(diǎn)的例子——模板查詢,當(dāng)然,它所完成的功能也更加的強(qiáng)大。例如下面的例子中t()代表調(diào)用模板查詢,t(a)表達(dá)的是設(shè)定當(dāng)前模板別名為a,從頂點(diǎn)12開(kāi)始,經(jīng)過(guò)一條邊抵達(dá)到屬性age值為20的頂點(diǎn)b(別名),返回這個(gè)模板所匹配的結(jié)果a和抵達(dá)頂點(diǎn)b的名字。
和傳統(tǒng)SQL類(lèi)似的地方是可以對(duì)任何過(guò)濾條件設(shè)置別名,不同之處在于當(dāng)異構(gòu)結(jié)果a和b.name一同被返回的時(shí)候,a表達(dá)的是整個(gè)模板搜索所對(duì)應(yīng)的路徑結(jié)果的集合,而b.name則是一組頂點(diǎn)的屬性的數(shù)組集合,如下面兩張圖所示sql 數(shù)據(jù)庫(kù)關(guān)系圖,這種異構(gòu)靈活性是SQL不具備的。
圖:鏈?zhǔn)綀D查詢語(yǔ)言之模板化路徑查詢
圖:模板路徑查詢中返回的異構(gòu)結(jié)果集
下面,我們?cè)儆靡粋€(gè)例子來(lái)說(shuō)明在圖查詢中使用簡(jiǎn)單的查詢語(yǔ)言實(shí)現(xiàn)深度的、遞歸式的查詢:
t(a1).n(n1{age:20}).e(e1{rank:{$bt:[20,30]}})[3:7].n(n2).limit(50).return(a1, n1, e1, n2._id, n2.name)
這個(gè)語(yǔ)句中,從年齡=20歲的頂點(diǎn)(可能有多個(gè))出發(fā),進(jìn)行深度為3至7層的路徑搜索查詢抵達(dá)某些頂點(diǎn),并且路徑中每條邊的權(quán)重介于20至30之間,找到50條路徑,并返一系列異構(gòu)的數(shù)據(jù)(模板匹配的路徑本身、起始頂點(diǎn)、邊、終止定點(diǎn)的兩個(gè)屬性)。
這種靈活度在SQL當(dāng)中,如果不通過(guò)書(shū)寫(xiě)大量的封裝代碼是很難實(shí)現(xiàn)的,而且這種搜索深度也是令關(guān)系型數(shù)據(jù)庫(kù)望而卻步的—通常會(huì)發(fā)生因內(nèi)存或系統(tǒng)資源耗盡而導(dǎo)致數(shù)據(jù)庫(kù)出現(xiàn)SEG-FAULT。
任務(wù)3:數(shù)學(xué)統(tǒng)計(jì)類(lèi)型的查詢,例如count( ),sum( ), min( ), ( )等。
例1:這個(gè)例子對(duì)于SQL編程愛(ài)好者而言一點(diǎn)都不陌生——統(tǒng)計(jì)一家公司員工的工資總和。
t(p).n(12).le({type:"works_for"}).n(c{type: "human"}).return(sum(c.salary))
在 GQL中也是一句話即實(shí)現(xiàn):
·從公司頂點(diǎn)12出發(fā)
·找到所有工作于(邊關(guān)系)本公司的員工,別名為c
·返回他們?nèi)抗べY之和
在一張小表中,這個(gè)操作在SQL語(yǔ)境下同樣毫無(wú)壓力,但是在一張大表中(千萬(wàn)或億萬(wàn)行),或許這個(gè)SQL操作就會(huì)因?yàn)楸頀呙瓒兊镁徛恕?/p>
而在數(shù)據(jù)庫(kù)中因?yàn)椴捎孟噜徆?近鄰存儲(chǔ)的存儲(chǔ)邏輯及并發(fā)邏輯優(yōu)化,這種面向一步抵達(dá)的鄰居頂點(diǎn)的數(shù)學(xué)統(tǒng)計(jì)操作幾乎不會(huì)受到數(shù)據(jù)集大小的影響,進(jìn)而可以讓任務(wù)執(zhí)行時(shí)間基本恒定。
例2:統(tǒng)計(jì)該公司的員工都來(lái)自于哪幾個(gè)省:
t(p).n(12).e({type:"works_for"}).n(c{type:"employee"}).return(collect(c.province))
上面的兩個(gè)例子是來(lái)說(shuō)明通過(guò) GQL的方式可以實(shí)現(xiàn)傳統(tǒng)關(guān)系型SQL查詢所能實(shí)現(xiàn)的功能。同樣,返回結(jié)果也可以以關(guān)系型數(shù)據(jù)庫(kù)查詢結(jié)果所常用的表單、表格的方式來(lái)呈現(xiàn),例如下面兩張圖所示:
圖:在 中以表格的方式展示結(jié)果列表
在上圖中,khop( )操作返回的是從初始頂點(diǎn)出發(fā)經(jīng)過(guò)depth( ) 限定的深度搜索后返回的第K層的鄰居的集合,使用( )可選定需要具體返回的屬性。
下圖中展示的是類(lèi)似的操作在 CLI中返回的結(jié)果示例。注意該圖中的時(shí)間有兩個(gè)維度,引擎時(shí)間和全部時(shí)間,其中引擎時(shí)間是內(nèi)存圖計(jì)算引擎的運(yùn)算耗費(fèi)時(shí)間,而全部時(shí)間還包括一些持久化存儲(chǔ)層的數(shù)據(jù)轉(zhuǎn)換的時(shí)間。
圖: 在-CLI中以表單的方式返回結(jié)果集
任務(wù)4:強(qiáng)大的基于模板的全文搜索。
如果一個(gè)數(shù)據(jù)庫(kù)系統(tǒng)中不能支持全文搜索,那么我們很難能稱(chēng)其為完整的數(shù)據(jù)庫(kù)。在圖數(shù)據(jù)庫(kù)支持全文搜索并不是一個(gè)全新的事情,例如老牌的圖數(shù)據(jù)庫(kù)Neo4j中通過(guò)集成 的全文搜索框架,讓用戶可以通過(guò)語(yǔ)句來(lái)對(duì)頂點(diǎn)(及其屬性)進(jìn)行全文本搜索。
但是,這種集成開(kāi)源框架的方式存在一個(gè)嚴(yán)重的副作用,就是性能預(yù)期與實(shí)際(查詢)操作中的落差——圖查詢中關(guān)注的往往是多層、深度的路徑或K-鄰查詢,而全文搜索匹配僅僅是這類(lèi)查詢的第一步,試想,當(dāng)系統(tǒng)集成了外部開(kāi)源框架后,多套子系統(tǒng)間就存在頻繁的交互和網(wǎng)絡(luò)時(shí)延,這種查詢的效率可想而知。另一個(gè)原因是開(kāi)源的框架可能存在一些不可預(yù)知的一些問(wèn)題,在生產(chǎn)環(huán)境中一旦暴露,修復(fù)起來(lái)非常困難,這個(gè)或許可被看作是開(kāi)源的一個(gè)重大弊端吧。
在 GQL 中完成面向頂點(diǎn)的全文搜索,只需要下面這句簡(jiǎn)單的查詢語(yǔ)句:
find().nodes(~name:"Sequoia*").limit(100).select(name,intro)
這句返回的是找到100個(gè)包含“”字樣的頂點(diǎn),并返回它們的name和intro屬性。這個(gè)查詢非常類(lèi)似于傳統(tǒng)數(shù)據(jù)庫(kù)中的面向某張表的列信息查詢。同樣地,也可以針對(duì)邊來(lái)進(jìn)行查詢,例如下面:
find().edges(~name:"Love*").limit(200).select(*)
找到圖中所有的邊上的name屬性中存在“愛(ài)情”字樣的關(guān)系。
當(dāng)然,如果我們的全文檢索只是停留在點(diǎn)、邊查詢,那么這就略顯單薄了。在真實(shí)的商用化的圖數(shù)據(jù)庫(kù)應(yīng)用場(chǎng)景中,我們更可能用一種基于模板的模糊匹配全文查詢。
例如,模糊的搜索從“紅杉***”出發(fā)到“招銀*”的一張關(guān)聯(lián)關(guān)系網(wǎng)絡(luò),網(wǎng)絡(luò)中的路徑搜索深度不超過(guò)5層,返回20條路徑所構(gòu)成的子圖。注意:這個(gè)搜索從模糊匹配頂點(diǎn)出發(fā),到達(dá)模糊匹配的另外一套頂點(diǎn)。
t().n(~name: "紅杉*").e()[:5].n(~name: "招銀*").limit(20).select(name)
如果不用上面這句簡(jiǎn)單得不能再簡(jiǎn)單的 GQL,你能想象如何用其它SQL或NoSQL語(yǔ)言來(lái)實(shí)現(xiàn)嗎?
假設(shè)我們?cè)谝粋€(gè)工商數(shù)據(jù)集之上,在天眼查、企查查做類(lèi)似的查詢,你要先找到名字中包含有“紅杉”或“招銀”字樣的公司,然后再分別對(duì)每一家公司的投資關(guān)系進(jìn)行梳理,需要查清楚每家被投公司的合作、競(jìng)爭(zhēng)、董監(jiān)高等關(guān)系,然后再慢慢梳理出來(lái)是否能在5步之內(nèi)關(guān)聯(lián)上名稱(chēng)中包含“紅杉”字樣的一家公司和包含“招銀”字樣的另一家公司。這個(gè)操作絕對(duì)是讓人瘋狂的,你可能需要花費(fèi)數(shù)天的時(shí)間來(lái)完成,或者能夠通過(guò)寫(xiě)代碼調(diào)用API的方式來(lái)“智能化”的實(shí)現(xiàn)。無(wú)論如何,你很難在下面兩件事情上擊敗 GQL:
圖:實(shí)時(shí)地基于模板查詢的全文搜索( )
在上圖中,這個(gè)看起來(lái)簡(jiǎn)單而又實(shí)際上非常復(fù)雜的查詢操作僅僅耗時(shí)50ms!這種復(fù)雜查詢的效率是前所未有的。如果讀者知道有任何其它數(shù)據(jù)庫(kù)系統(tǒng)可以在更短的時(shí)間內(nèi)完成同樣的操作,歡迎聯(lián)系筆者深度交流。
一門(mén)先進(jìn)的(數(shù)據(jù)庫(kù))查詢語(yǔ)言的優(yōu)美感,不是通過(guò)它到底有多復(fù)雜,而是通過(guò)它有多簡(jiǎn)潔來(lái)體現(xiàn)的。它應(yīng)該具備這樣的一些通性:
特別是最后一點(diǎn),如果讀者對(duì)于SQL或、 或中復(fù)雜的嵌套邏輯心有余悸,你會(huì)更理解下面的這個(gè)比喻:當(dāng)古希臘神話中的泰坦Atlas 把整個(gè)世界(地球)抗在他的肩膀上的時(shí)候,世界公民們(數(shù)據(jù)庫(kù)用戶)并不需要去感知這個(gè)世界有多沉重(數(shù)據(jù)庫(kù)有多復(fù)雜)。
任務(wù)5:復(fù)雜的圖算法。
相比于其他數(shù)據(jù)庫(kù)而言,圖數(shù)據(jù)庫(kù)的一個(gè)明顯優(yōu)勢(shì)是集成化的算法功能支持。圖上有很多種算法,例如出入度、中心度、排序、傳播、連接度、社區(qū)識(shí)別、圖嵌入、圖神經(jīng)元網(wǎng)絡(luò)等等。隨著商用場(chǎng)景的增多,相信會(huì)有更多的算法被移植到圖上或者被發(fā)明創(chuàng)造出來(lái)。
以魯汶社區(qū)識(shí)別算法為例,這個(gè)算法出現(xiàn)的時(shí)間僅僅十幾年,它得名于它的誕生地——比利時(shí)法語(yǔ)區(qū)的魯汶大學(xué)( )。
它最初被發(fā)明的目的是用來(lái)通過(guò)復(fù)雜的多次遞歸遍歷一張由社交關(guān)系屬性構(gòu)成的大圖中的點(diǎn)、邊來(lái)找到所有的頂點(diǎn)(例如人、事、物)所構(gòu)成的關(guān)聯(lián)關(guān)系社區(qū),緊密關(guān)聯(lián)的頂點(diǎn)會(huì)處于同一社區(qū),不同的頂點(diǎn)可能會(huì)處于不同的社區(qū)。在互聯(lián)網(wǎng)、金融科技領(lǐng)域,魯汶算法受到了相當(dāng)?shù)闹匾暋O旅孢@行 GQL 語(yǔ)句完成了魯汶算法的調(diào)用執(zhí)行:
algo().louvain({phase1_loop:5, min_modularity_increase:0.01})
在圖數(shù)據(jù)庫(kù)中,調(diào)用一個(gè)算法與執(zhí)行一個(gè)API調(diào)用類(lèi)似,都需要提供一些必須的參數(shù)。上例中,用戶僅需提供最少兩個(gè)參數(shù)就可以執(zhí)行魯汶。當(dāng)然,用戶也可以設(shè)定更為復(fù)雜的參數(shù)集來(lái)優(yōu)化魯汶算法,因篇幅所限,本文在此并不做過(guò)多展開(kāi)描述。
魯汶算法因其天然的邏輯復(fù)雜性,計(jì)算結(jié)果與效果如果能通過(guò)可視化的方式來(lái)呈現(xiàn),會(huì)起到事半功倍的效果。在下圖中,我們展示了一種在魯汶算法執(zhí)行過(guò)程中自動(dòng)化生成的數(shù)據(jù)集,可以支持基于全量數(shù)據(jù)或抽樣數(shù)據(jù)的實(shí)時(shí)算法結(jié)果3D可視化。
圖:實(shí)時(shí)的魯汶社區(qū)識(shí)別算法及Web可視化( )
注:
原生的魯汶社區(qū)識(shí)別算法的實(shí)現(xiàn)是串行的,也就是說(shuō)它需要從全圖中的所有頂點(diǎn)出發(fā),逐個(gè)頂點(diǎn)、逐條邊的逐條邊進(jìn)行反復(fù)運(yùn)算。例如在的庫(kù)中,對(duì)一個(gè)普通的(幾十萬(wàn)至幾百萬(wàn)頂點(diǎn))圖數(shù)據(jù)集進(jìn)行魯汶運(yùn)算要耗時(shí)數(shù)個(gè)、數(shù)十個(gè)小時(shí),但是在 Graph上,這個(gè)計(jì)算的耗時(shí)通過(guò)高度并發(fā)被劇烈的縮短到了毫秒到秒級(jí)。在這里,我們探討的不是10倍至100倍的性能超越,而是成千上萬(wàn)倍的性能提升。果讀者覺(jué)得筆者給出的案例是天方夜譚或?yàn)榘V人說(shuō)夢(mèng),或許你應(yīng)當(dāng)重新審視一下你對(duì)于系統(tǒng)架構(gòu)、數(shù)據(jù)結(jié)構(gòu)、算法以及它們的最優(yōu)工程實(shí)現(xiàn)的理解了。
圖查詢語(yǔ)言還可以支持很多功能強(qiáng)大且智能化很高深的操作,上面的5個(gè)例子只起到了一個(gè)拋磚引玉的作用,筆者希望它們能揭示GQL的簡(jiǎn)潔性,并喚起讀者去思考一個(gè)問(wèn)題:你到底是愿意去絞盡腦汁地書(shū)寫(xiě)成百上千行的SQL代碼,并借此殺死大量腦細(xì)胞來(lái)讀懂代碼,還是考慮用更簡(jiǎn)潔、方便卻更加強(qiáng)大的圖查詢語(yǔ)言呢?
關(guān)于數(shù)據(jù)庫(kù)查詢語(yǔ)言,筆者認(rèn)為:
有些人包括一些知名的投資機(jī)構(gòu)和行業(yè)“專(zhuān)家”認(rèn)為,關(guān)系型數(shù)據(jù)庫(kù)和SQL永遠(yuǎn)也不會(huì)被取代。筆者認(rèn)為這種看法禁不起推敲,如果我們稍微回顧一下不是很久遠(yuǎn)的歷史就會(huì)發(fā)現(xiàn),關(guān)系型數(shù)據(jù)庫(kù)從20世紀(jì)80年代開(kāi)始取代了之前的導(dǎo)航型數(shù)據(jù)庫(kù),已經(jīng)稱(chēng)霸了行業(yè)近半個(gè)世紀(jì)了,但是它們?cè)絹?lái)越難以滿足不斷迭代與前進(jìn)的業(yè)務(wù)需求。
如果歷史真正教會(huì)我們一些常識(shí),那就是對(duì)于任何事情的執(zhí)著和癡迷都不會(huì)長(zhǎng)久,特別是在這個(gè)科技蓬勃發(fā)展且不斷推陳出新的時(shí)代。
·END·