一篇看懂圖數據庫janusgraph存儲結構

簡介

JanusGraph是以鄰接列表存儲的,這意味着圖形存儲爲鄰接列表的頂點集合。頂點的鄰接列表包含全部點的邊(出邊和入邊,包含邊的屬性)和頂點對應的屬性。 JanusGraph按排序順序維護鄰接列表中的每一個頂點,其順序由排序鍵和邊標籤的排序順序定義。排序順序容許使用以頂點爲中心的索引有效地檢索鄰接列表的子集。 JanusGraph圖形的鄰接列表能夠存儲在支持Bigtable數據模型的任何存儲後端中。 對於圖形的存儲,對於圖的拆分格式有兩種方式:按點切割、按邊切割。html

  • 按點切割:根據點進行切割,每一個邊只存儲一次
  • 按邊切割:根據邊進行切割,以節點爲中心,邊會存儲兩次,JanusGraph就是採用的按邊切割,按邊切割後以鄰接列表的形式存儲圖形。

經過以鄰接列表格式存儲圖形,JanusGraph確保全部頂點的邊和屬性緊密地存儲在存儲後端中(好比Hbase\Cassandra等),從而能夠加速遍歷。缺點是每一個邊必須存儲兩次一次做爲source頂點的出邊被存儲,一次做爲target頂點入邊被存儲,固然這也使咱們能夠在source Vertex和target Vertex中任意頂點均可以快速找到對端。java

Bigtable數據模型:

來自官網的示意圖: sql

在這裏插入圖片描述
在Bigtable數據模型中,每一個表是行的集合,由一個key惟一標識。每行由任意(能夠很大數量可是必須有限數量)數量的cell組成。cell由column和value組成。cell由給定行內的column惟一標識。 cell(單元格)、column(列)、value(列值) Bigtable模型中的行稱爲「寬行」,由於它們支持大量cell,而且沒必要像關係數據庫中那樣預先定義這些cell的column。在關係型數據庫中咱們必須先定義好表的schema,才能夠存儲數據,若是存儲過程當中想要改變表結構,則全部的數據都要對變化的列作出變化。可是Bigtable模型存儲中就沒必要如此,每一個行的column不一樣,咱們能夠隨時僅對某一行進行變化,也不準預先定義行的schema,只須要定義圖的schema便可。 JanusGraph對Bigtable數據模型有一個額外要求:存儲邊的單元格必須按column排序,而且列範圍指定的單元格子集必須是有效可檢索的(例如,經過使用索引結構,跳過列表或進行二進制搜索)。 此外,特定的Bigtable實現可使行按其鍵的順序排序。JanusGraph能夠利用這樣的鍵序來有效地劃分圖形,從而爲很是大的圖形提供更好的加載和遍歷性能。可是,這並非必需的。

JanusGraph的存儲:

來自官網的示意圖: 數據庫

在這裏插入圖片描述
前面說過,JanusGraph使用Bigtable模型進行存儲數據,若是使用的存儲後臺支持鍵順序(如Hbase),則鄰接列表將按Vertex Id排序進行順序存儲的。 在JanusGraph存儲數據的表中,行的惟一key能夠是任意字符串(目前最大爲64KB,儘管用戶大多數只使用10-100字節,下面介紹了vertex id的組成)。每次在一行中讀或寫數據都是一個原子操做(儘管一行中不一樣列可能正在進行讀或寫),這個設計使客戶端能夠更加方便的推導出在併發更新相同行時的系統行爲。 在JanusGraph中,是點爲中心,按切邊的方式存儲數據的。好比在Hbase中節點的ID做爲HBase的Rowkey,節點上的每個屬性和每一條邊,做爲該Rowkey的一個個獨立的Cell。即每個屬性、每一條邊,都是一個個獨立的KCV結構(Key-Column-Value)。

1:具體案例

好比咱們下面有一個圖, 後端

在這裏插入圖片描述
拆分定義後爲:
在這裏插入圖片描述
而後其存儲格式就基本就能夠肯定了。

2:vertex id的組成:

  1. Vertex ID以Rowkey的形式存儲在HBase中,Vertex ID共包含64個bit。
  2. Vertex ID由partition id、count、ID padding三部分組成。 2. Vertex ID由partition id、count、ID padding三部分組成。
  3. 最高位5個bit是partition id。partition是JanusGraph抽象出的一個概念。當Storage Backend是HBase時,JanusGraph會根據partition數量,自動計算並配置各個HBase Region的split key,從而將各個partition均勻映射到HBase的多個Region中。而後經過均勻分配partition id最終實現數據均勻打散到Storage Backend的多臺機器中。
  4. 中間的count部分是流水號,其中最高位比特固定爲0.
  5. 最後幾個bit是ID padding, 表示Vertex的類型。具體的位數長度根據不一樣的Vertex類型而不一樣。最經常使用的普通Vertex,其值爲'000'。

其組成圖爲(騰訊雲社區): 數組

在這裏插入圖片描述

3:邊和屬性在cell中具體的存儲形式

下面來自官網的示意圖: bash

在這裏插入圖片描述

深藍色框表示使用可變長度編碼方案編碼的數字,以減小它們消耗的字節數。紅框表示使用關聯屬性鍵中引用壓縮元數據序列化的一個或多個屬性值(即對象)。灰色框表示未壓縮的屬性值(即序列化對象)數據結構

上圖中咱們能夠看出,Edge和Property在cell中都是由column(列)和value(值)組成。 Edge中column由labelid(邊標籤id)+direction(邊的方向,相對於節點的出邊或者入邊)+sort key(用於邊排序的key)+adjacent vertex id(臨近頂點的id)+edge id(邊id)組成,value由signature key(簽名密匙)+other properties(邊的其餘屬性)組成。併發

注意:此處的組成元素SortKey是一種特殊的屬性,JanusGraph容許在定義Edge Label時指定其中的一個或多個屬性爲Sort Key,主要的做用是:將數據序列化進行存儲時,序列化中edge會根據你設定的sort key進行排序,好比A有多個朋友關係的edge,每個edge都有一個創建時間(createtime),sort key即可以在存儲時將邊按照創建時間進行順序存儲,這樣便於查找某個createtime的邊,也便於範圍查找。post

此處的signature key觀看源碼其實就是一個list數組, 裏面存儲的是邊的property的key id(注意不是property id),做用是:邊的other properties是被序列化存儲在磁盤中,當咱們查找 邊是否包含某一屬性時不可能將其序列化回來再進行查找,這時候signature key的做用就體現出來了,經過其就能夠知道這條邊有什麼屬性,就能夠更快的進行查找。如今再看‘紅框表示使用關聯屬性鍵中引用壓縮元數據序列化的一個或多個屬性值’這句話,說的就是我引用屬性的key id 。 signature key有一種空間的換時間的感受。。

Property中column由key id(屬性的鍵id)組成,value由屬性id+屬性值組成。

這裏注意key id 和 property id,key id 是屬性key的id,舉個栗子:name:李陽,這裏的key id就是name這個property key的id,而name:李陽總體有一個id就是property id 了

4:其中對於property的存儲:

一個Property Key所關聯的屬性值有可能有一個,也有可能有多個,所以,JanusGraph使用Cardinality來描述Property Key的這種特色。有SINGLE,LIST和SET三種類型, 源碼中的PropertyKey接口能夠看到:

public interface PropertyKey extends RelationType {
    Class<?> dataType();
    Cardinality cardinality();
}
複製代碼
  • SINGLE表示一個Property Key只對應一個Value,這是最經常使用的場景。

Cardinality爲SINGLE時的存儲結構,HBase的列名只存儲Property Key的ID。具體的Property Value值以及Property ID(JanusGraph爲每個Property分配的惟一ID),都存放在Cell的Value中。另外,若是該Property還有額外的Remaining properties,也會放在Value中。Remaining properties通常不使用,僅在一些特殊場景下,用於爲該Property記錄更多的附加信息(好比存儲元數據Edge Labe的定義等)。

  • LIST表示一個Property Key能夠對應多個Value,多個Value能夠有重複值。

Candinality爲LIST時的存儲結構,各個部分與Cardinality爲SINGLE時的結構類似,區別在於屬性的ID被放在了列名中,而不是放在Value中。

  • SET表示一個Property Key能夠對應多個Value,多個Value不能夠有重複值。

Candinality爲SET存儲結構,各個部分與Cardinality爲SINGLE時的結構類似,區別在於屬性的值被放在了列名中,而不是放在Value中,前提是去除了重複。

JanusGraph的Property,在不一樣的Cardinality下,數據存儲結構略有不一樣。總體原則是:根據Cardinality的不一樣,列名自己可以肯定惟一的一個屬性便可。

5:Edge label 的多樣性

默認的多重性是MULTI

  • MULTI:容許任意一對頂點之間的同一標籤具備多個邊。
  • SIMPLE:在任何一對頂點之間最多容許擁有此類標籤的一個邊。
  • MANY2ONE:在圖形中的任何頂點上最多容許此標籤的一個傳出邊,但不對傳入邊施加約束。邊標籤mother是MANY2ONE多樣性的一個例子,由於每一個人最多隻有一個母親,但母親能夠有多個孩子。
  • ONE2MANY:在圖形中的任何頂點上最多容許此標籤的一個傳入邊,但不對傳出邊施加約束。邊標籤winnerOf是ONE2MANY多樣性的一個例子,由於每一個比賽最多隻贏一我的,但一我的能夠贏得多個比賽。
  • ONE2ONE:在圖表的任何頂點上最多容許此標籤的一個傳入邊和一個傳出邊。邊標籤結婚是ONE2ONE多樣性的一個例子,由於一我的與另外一我的結婚。

Property Key和Edge Label被抽象成了Relation Type,並採用相同的數據結構。因此說 Property Key和Edge Label不能被設置爲相同的名字。

6:序列化:

每一個邊和屬性都做爲一個cell存儲在其相鄰頂點的行中。它們會被序列化而且column的字節順序會遵循edge 的column中的sort key進行存儲。變量id編碼方案和壓縮對象序列化使每一個edge/cell的存儲所佔空間都儘量小。 邊的序列化從邊標籤的惟一ID開始(由JanusGraph指定)。這一般是一個小數字,而且使用變量id編碼進行壓縮。該id的最後一位是偏移量,用於存儲這個邊是入邊(in)仍是出邊(out)。接下來,存儲包括sort key 的屬性值,它是使用edge label定義的,所以排序鍵對象元數據能夠引用邊緣標籤。以後,存儲相鄰頂點的id。JanusGraph不存儲實際的頂點id,而是存儲擁有此鄰接列表的頂點的id。頂點id後跟此邊的id,JanusGraph爲每條邊分配了一個惟一的id。這就是edge cell的column。edge的cell的column包含邊的壓縮序列化後的簽名屬性(由標籤的簽名鍵定義)以及未壓縮序列化的邊的任何其餘屬性。 屬性的序列化表示更簡單,column中只包含屬性的鍵ID。屬性id和屬性值存儲在value中。可是,若是將屬性鍵定義爲list()或者set(),則property id也存儲在column中(上面property存儲中說到過)。

JanusGraph Schema:

從上述來看,咱們能夠知道,JanusGraph圖的schema該怎樣定義主要是由edge labels 、property keys 和vertex labels 組成(Each JanusGraph graph has a schema comprised of the edge labels, property keys, and vertex labels used therein),JanusGraph的schema能夠顯式或隱式建立,推薦用戶採用顯式定義的方式。JanusGraph的schema是能夠在使用過程當中修改的,並且不會致使服務宕機,也不會拖慢查詢速度。,好比一個簡單的顯示定義的銷售圖的scheme:

<propertyKey value="salesman_id" explain="銷售人員id" index="true" type="java.lang.String" />
<propertyKey value="real_name" explain="姓名" index="" type="java.lang.String" />
<propertyKey value="role" explain="角色" type="" />
<propertyKey value="city_code" explain="所處城市代碼" index="" type="" />
<propertyKey value="create_time" explain="建立時間" index="" type="" />

<edgeLabel value="saleman_service_for" explain="銷售引導">
    <propertys>
        <property value="create_time"/>
    </propertys>
</edgeLabel>
<edgeLabel value="own_salaman_Idcard" explain="銷售身份">
    <propertys>
        <property value="create_time"/>
    </propertys>
</edgeLabel>

<index elementType="vertex" indexType="compositeIndex" name="salesman_id_I" >
    <propertyKeys>
        <propertyKey value="salesman_id" />
    </propertyKeys>
</index>

<vertexLabel value="salesman" explain="銷售" >
    <propertys>
        <property value="salesman_id" />
        <property value="real_name" />
        <property value="role" />
        <property value="city_code" />
    </propertys>
    <edges>
        <edge value="saleman_service_for" direction="out" />
        <edge value="own_salaman_Idcard" direction="out" />
    </edges>
</vertexLabel>
複製代碼

固然,咱們也能夠添加一些其餘的能夠組成schema的元素,上述三個是必須的,另外的好比索引(index)等,主要的結構仍是:

JanusGraph Schema
            |-----------Vertex Lables
            |-----------Property Keys
            |-----------Edge Labels
複製代碼

和通關係型數據庫不一樣,圖數據的schema是定義一張圖,而非定義一個vertex的。在Mysql中,咱們一般將創建一張表定義爲建立一個schema,而在JanusGraph中,一個Graph用於一個schema。

refer:博客 博客

若是轉載此博文,請附上本文連接,謝謝合做~ :juejin.im/user/5c3036…

若是感受這篇文章對您有所幫助,請點擊一下「喜歡」或者「關注」博主,您的喜歡和關注將是我前進的最大動力!

相關文章
相關標籤/搜索