Dgraph概念

1.概念

1.1 XID <-> UID

所有實體會被分配一個唯一的64位整型id,即UID。如果某個尸體有一個外部id(external id),即XID,DGraph會取出XID的指紋,并保存對應的UID。如果某個實體沒有XID,DGraph只會添加一個新的UID,并標明這個UID已用。所有的posting list都通過UID引用實體。由于UID是8字節(jié)的整數,這種數據表示非常高效。在數據導入的時候,我們需要每個唯一的實體有一個唯一的UID。

1.2 Edges

典型的數據格式是RDF NQuad:

  • 主語(Subject), 謂語(Predicate), 賓語(Object), 標簽(Label), aka
  • 實體(Entity), 屬性(Attribute), 另一個實體或值(Other Entity / Value),標簽(Label)

兩種專門用語在Dgraph的代碼里可以交替使用。Dgraph的edge是有向的,例如Subject -> Object。這也是查詢執(zhí)行的方向

可以自動生成一個反向的邊。如果用戶想按相反的方向執(zhí)行查詢,需要將reverse edge定義為schema的一部分

在Dgraph內部,RDF NQuad倍解析為下面的格式:

type DirectedEdge struct {
  Entity      uint64
  Attr        string
  Value       []byte
  ValueType   uint32
  ValueId     uint64
  Label       string
  Lang        string
  Op          DirectedEdge_Op // Set or Delete
  Facets      []*facetsp.Facet
}

不考慮輸入的話,Entity以及 Object/ValueId都通過上面的XID <-> UID被轉化成UID格式。

1.3 Posting List

概念上,posting list包含與一個Attribute關聯的所有有向邊,通過如下格式:

Attribute: Entity -> sorted list of ValueId 
//Everything in uint64 representation.

例如,如果我們存儲一個朋友的列表,例如:

Entity Attribute ValueId
Me friend person0
Me friend person1
Me friend person2
Me friend person3

那么,將生成一個friend的posting list。在這個PL中查找Me會生成一個friend的列表,即[person0, person1, person2, person3]

擁有這個結構的一個很大優(yōu)勢是可以把需要做一個join的所有數據保存在一個PL中。這意味著,一個到持有PL的服務器RPC調用的結果是一個連接,而不需要更多的網絡調用,減少查詢需要的連接

顧名思義,PL是Posting的列表,用Protocol Buffers表示的話,它是這個樣子:

message Posting {
  fixed64 uid = 1;
  bytes value = 2;
  enum ValType {
    DEFAULT = 0;
    BINARY = 1;
    INT = 2; // We treat it as int64.
    FLOAT = 3;
    BOOL = 4;
    DATE = 5;
    DATETIME = 6;
    GEO = 7;
    UID = 8;
    PASSWORD = 9;
    STRING = 10;

  }
  ValType val_type = 3;
  enum PostingType {
    REF=0;          // UID
    VALUE=1;        // simple, plain value
    VALUE_LANG=2;   // value with specified language
    // VALUE_TIMESERIES=3; // value from timeseries, with specified timestamp
  }
  PostingType posting_type = 4;
  bytes metadata = 5; // for VALUE_LANG: Language, for VALUE_TIMESERIES: timestamp, etc..
  string label = 6;
  uint64 commit = 7;  // More inclination towards smaller values.
  repeated facetsp.Facet facets = 8;

  // TODO: op is only used temporarily. See if we can remove it from here.
  uint32 op = 12;
}

message PostingList {
  repeated Posting postings = 1;
  bytes checksum = 2;
  uint64 commit = 3; // More inclination towards smaller values.
}

Dgraph中的所有數據都會首先使用Protocol Buffers序列化為字節(jié)數組后被存儲或傳輸。當結果被返回給用戶的時候,protocol buffer對象會被轉換為JSON對象

在一個PL中通常有超過一個的Posting

RDF標簽在每個posting中用label表示

現在Dgraph還不能通過查詢獲得label,但是將來會可以的

1.4 Badger

PostingList是通過Badger提供服務的,BadgerDB是一個嵌入式的、持久化的、簡單的快速鍵值數據庫,它使用純Go語言開發(fā).BadgerDB決定應該從內存、SSD或磁盤上提供多少數據。此外,它還支持在key上使用布隆過濾器,使隨機查詢的效率更高

為了讓Badger對內存有完全的訪問權限,以優(yōu)化cache,每臺服務器上都有一個Badger。每個實例包含這臺服務器上的所有posting list

Posting list在Badger上以鍵值對的格式存儲,例如:

(Predicate, Subject) --> PostingList

1.5 Group

包含同一個謂語(Predicate)的那些Posting list組成了一個group。每臺服務器可以存儲多個不同的group。

Group的配置文件用于確定每臺服務器應該保存哪些group。在將來的版本中,在線的Dgraph服務器將可以使用探試法(heuristics)移動標簽(tablets)

如果某個group變的太大,它可以被分割。在這種情況下,一個謂語實際上被分到兩個group上,像下面這樣:

Original Group:
            (Predicate, Sa..z)
  After split:
  Group 1:  (Predicate, Sa..i)
  Group 2:  (Predicate, Sj..z)

注意,這些key是被存儲在RocksDB里的,因此group的split將會保持排序。例如,按字段順序排序,在主語(subject)之前的被分到一個分組里,在主語之后的分到另一個分組里

1.6 復制與服務器故障

如果可能的話,每個group至少需要被三個服務器保存。當某臺服務器故障時,其他保存同一個group數據的服務器可以繼續(xù)提供服務

1.7 新服務器及發(fā)現

Dgraph集群可以自動檢測加入到集群中的新服務器,建立連接,并將新服務器應該保存的group數據發(fā)過去

1.8 預寫日志

對數據庫的寫入不會立馬通過RocksDB寫到磁盤上,而是先寫到磁盤上的預寫日志(Write ahead logs)里,這樣可以避免posting list的經常重建

1.9 修改

除了被寫入預寫日志之外,對數據庫的修改還會被存儲在內存中,作為不可變的Posting list之上的一個可變層。它允許用戶遍歷Postings,仿佛它們是被排過序的,而無需重建posting list

當一個posting list在內存中有被修改后,它被視為dirty的。Dgraph會周期性地重新生成不可變的版本,并將改變寫入RocksDB。注意,寫入RocksDB的過程是異步的,這意味著它們并不會立刻被刷寫到磁盤,但在服務器宕機的時候,這可能會導致數據丟失。但是不用擔心,在Posting list被初始化的時候,會讀取預寫日志,這時丟失的數據會被補上

每當重新生成posting list的時候,Dgraph還會寫一個包含最后一次提交日志的時間戳,用來決定在初始化posting list的時候從預寫日志回溯多久的數據。(Every time we regenerate a posting list, we also write the max commit log timestamp that was included – this helps us figure out how long back to seek in write-ahead logs when initializing the posting list, the first time it’s brought back into memory.)

1.10 事務

Dgraph現在還不支持事務。

0.9版本已經支持事務

With the availability of transactions, a user can query for data, and then write their mods back atomically. Thus, upsert and mutation variables can be done via client-side logic, instead of baking this limiting functionality in the server.

一個修改可以由多條邊組成,每條邊可能屬于不同的Posting list。Dgraph需要RWMutex來提供posting list上的鎖。而在多個posting list上不存在鎖

這意味著,一些邊會比其他邊先寫入,而這時候讀的話,只能讀到部分被提交的數據。但是,還是有一致性保證的,當一個mutation成功的時候,任何成功的讀操作都可以讀到所有被更新的數據

如果一個mutation失敗了,由用戶決定是擦出部分寫入的數據,還是用正確的邏輯重寫。Dgraph的回復會說清楚哪些邊沒有被寫入成功,用戶可以設置正確的邊來重新寫入

局限性:

  • 你應該考慮好是否你的讀操作需要原子性的事務。除非你需要處理財務數據,這不是Dgraph的適用場景
  • 為了保證原子性,每個mutation只能有一條邊(RDF NQuads) 。這意味需要在clent及server之間反復地執(zhí)行多次網絡調用,這將影響你的寫入吞吐量,但是會使錯誤處理的邏輯變得更簡單

1.11版本

大體上來說,Dgraph存儲有兩種類型的數據,一種是關系(relationship)數據,一種是值

 Me friend person0    [Relation]
 Me name "Константи?н" [Value]

DGraph保存Posting list的方式,

2.最小化網絡調用

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容