- 產品
- 產品解決方案
- 行業解決方案
- 案例
- 數據資產入表
- 賦能中心
- 伙伴
- 關于
時間:2022-05-30來源:冰山控瀏覽數:528次
近五年來,圖數據庫在領域內熱度上升趨勢非常明顯,各個大廠與開源社區都推出了自己的圖數據庫。用戶規模比較大、有一定影響力的查詢語言包括Cypher、Apache開源項目的Gremlin等。從集群規模來看,過往有單機數據庫,現在大多圖數據庫都具備分布式能力,這就需要考慮數據的防丟失問題、主副本之間的一致性、多臺機器數據上的shard問題。
本文將圍繞以下五點展開:
了解圖數據庫
適用場景介紹舉例
數據模型和查詢語言
ByteGraph架構與實現
關鍵問題分析
01了解圖數據庫
目前,字節內部有如下表三款自研的圖數據產品。

1. 對比圖數據庫與關系數據庫
圖模型的基本元素包括點、邊和屬性。舉例:張三的好友所在的公司有多少名員工?傳統關系型數據庫需要多表join,而圖作為半結構化數據,在圖上進行遍歷和屬性的過濾會更加高效。
2. 什么是圖數據庫?
近五年來,圖數據庫在領域內熱度上升趨勢非常明顯,各個大廠與開源社區都推出了自己的圖數據庫。用戶規模比較大、有一定影響力的查詢語言包括Cypher、Apache開源項目的Gremlin等。從集群規模來看,過往有單機數據庫,現在大多圖數據庫都具備分布式能力,這就需要考慮數據的防丟失問題、主副本之間的一致性、多臺機器數據上的shard問題。
部分圖數據庫把圖數據庫與圖計算引擎二者合并在一起,目前字節內部采用的暫時分離的兩套系統。
02適用場景介紹舉例
1. ByteGraph適用的業務數據模型
ByteGraph初始立項是在2018年,主要目的是對頭條的用戶行為及好友關系進行存儲來替換Mysql;2019年6月承接對抖音用戶關系的數據存儲任務,接著在字節內部各種微服務重承接了相關業務。

2. 已上線業務場景分類
目前有1.5萬臺物理機,服務于600+業務集群。

1. 有向屬性圖建模
目前來看,圖數據庫通常有兩大類,一種是屬性圖,另一種是RDF圖。屬性圖在節點和邊上有屬性表,從某種角度上講,它仍帶有關系數據庫的基本特性,類似表結構的形式,實際是采用Key-Value形式來存儲的,如用戶A關注了用戶B,用戶C點贊了某個視頻等,則會把關注的時間、點贊時間、評論的內容等以不同的有向邊存儲在屬性圖中,用圖來描述業務邏輯。

2. Gremlin查詢語言接口
選用Gremlin語言是考慮到之后方便對圖計算、圖數據庫二者進行融合,本身是圖靈完備的圖遍歷語言,相較于Cypher等類SQL語言,對于善用Python的數據分析師更容易上手。
舉例:寫一條用戶A所有一跳好友中滿足粉絲數量大于100的子集。首先定位用戶A在圖中的點,其次求一跳查詢中的所有鄰居,判斷入度鄰居整體數量是否大于100,拉取滿足條件的所有用戶。

1. ByteGraph整體架構
ByteGraph整體架構分為查詢引擎層(Graph Query Engine,下文簡稱GQ)、存儲引擎層(Graph Storage Engine,下文簡稱GS)和磁盤存儲層三層,整體上計算和存儲分離,每層由多個進程實例組成集群。

2. ByteGraph讀寫流程
拿“讀流程”舉例,請求獲取用戶A的一跳鄰居。首先一個查詢進來后,從client端隨機挑選一個查詢層響應,對應到GQ2上,獲取對應的數據存放的位置是哪一臺機器,接著把請求給到GS1,檢查數據是否在該層以及是否為最新數據,如果不在則去KV store把所需數據拉取至GS1 緩存中。

3. ByteGraph實現:GQ
GQ同MySQL的SQL層一樣,負責查詢的解析和處理,其中的“處理”可以分為下述三個步驟:
Parser階段:利用遞歸下降解析器將查詢語言解析為一個查詢語法樹。
生成查詢計劃:將Parser階段得到的查詢語法樹按照查詢優化策略(RBO&CBO)轉換為執行計劃。
執行查詢計劃:理解GS數據分Partition的邏輯,找到相應數據并下推部分算子,保證網絡開銷不會太大,最后合并查詢結果,完成查詢計劃。
RBO主要基于Gremlin開源實現中的自帶優化規則、針對字節應用中的算子下推、自定義的算子優化(fusion)三大規則。CBO本質上是對每個點的出入度做統計,把代價用方程量化表示。

對于不同支持場景使用不同策略,圖分區算法的選擇與workload強相關,圖分區算法能有效減少網絡通信次數。
Brute force哈希分區:即根據起點和邊的類型進行一致性哈希分區,可以大部分查詢場景需求,尤其是一度查詢場景。
知識圖譜場景:點、邊類型極多,但每種類型邊數量相對較少,此時根據邊類型進行哈希分區,將同種邊類型數據分布在一個分區內。
社交場景:更容易出現大V,利用facebook于2016年提出的social hash算法,通過離線計算盡量將有關聯的數據放置在同一分片內,降低延遲。
4. ByteGraph實現:GS

存儲結構
單個Partition定義為一個起點+一種特定的邊類型扇出的一跳鄰居。在GS中,將一個Partition按照排序鍵(可顯式設置或系統默認維護)組織成Btree。每棵Btree都有獨立的WAL序列,獨立維護自增logid。這種設計有利于支持GNN場景,做分布式采樣。
Edge Page、Meta Page分別是位于Btree中的葉子結點、非葉子結點(充當index作用),分別用于存儲圖中的邊數據和指向子節點的Key。Meta page長度是固定的,但是一個meta page會放多少edge page是可配的,通常配置為2000一片。如上圖,Partition在磁盤中將每個page都存儲為一個獨立的鍵值對(下文簡稱KV対)。meta page的key是起點+邊類型,edge page的key存在meta page中實現對特定edge page的查找。
單機內存引擎整體采用hash map的結構,partition和page按需加載到內存中,根據LRU策略(Least Recent Used),swap到磁盤;某個page被修改后,WAL同步寫到磁盤,page會插入到dirty鏈表中,考慮當前機器狀態,異步寫回。

日志管理:單個起點+邊類型組成一棵Btree,每個結點是一個KV對。
每棵Btree單一寫者,防止并發寫入導致不完整;每棵樹都有獨立的WAL日志流,且寫入請求處理流程中只寫入WAL,并修改內存中數據,compaction時再將數據落盤,解決由于每個KV對可能由多條邊組成而導致的寫放大。即使內存數據丟失,仍可通過更新后的logid在磁盤上進行WAL的查詢并寫入。
緩存實現:根據不同場景及當下cpu的開銷有不同策略。
圖原生緩存:相對于Memcached等直接緩存二進制數據而言,能更好的理解圖的語義,并支持一度查詢中的部分計算下推功能。
高性能LRU Cache:支持緩存逐出,且逐出的頻率和觸發閾值可調;采用numa aware和cpu cacheline aware設計,提高性能;支持Intel AEP等新硬件。
Write-through cache:支持多種與底層存儲同步數據的模式,可以每次寫入或定時落盤;支持定期與底層存儲校驗數據,防止數據過舊;支持負緩存等常見優化策略。
緩存與存儲分離:當數據規模不變、請求流量增大的情況下,緩存與存儲分離的模式可以快速擴容緩存以提高服務能力。
05關鍵問題分析1. 索引
局部索引:給定一個起點和邊類型,對邊上的屬性構建索引
特點:邊上元素皆可做索引項,能夠加速查詢,提高屬性過濾和排序性能;但會額外維護一份索引數據,與對應的原數據使用同一條日志流,保證一致性。
全局索引:目前只支持點的屬性全局索引,即指定一個屬性值查詢出對應的點。
數據存儲在不同機器上,索引數據的一致性使用分布式事務解決。
2. 熱點讀寫
熱點讀
場景舉例:某熱點視頻被頻繁刷新,查看其點贊數量。
應用機制:GQ層采用多個bgdb并發處理同一熱點的讀請求,單節點緩存命中讀性能可達20萬以上;GS層采用copy on write(即先拷貝,再寫入并替換)保證讀寫、讀讀均可并發。
熱點寫
場景舉例:某熱點視頻短時間內被瘋狂轉發、點贊。
問題溯源:單機cpu使用率被拉高,磁盤寫入iops有上限,當客戶端寫入qps>磁盤iops時,就會發生請求排隊。
應對機制:采用group commit機制,即將多個寫入請求組合至一個batch寫入KV,再批量返回,降低磁盤層iops的上限。

3. 輕重查詢資源分配
將輕重查詢的資源池分離,輕查詢走light線程池,負責數量多的小查詢;重查詢則走heavy線程池,負責數量少的重查詢。當heavy線程池空閑時,輕查詢也可走。

4. 高可用
城域網雙機房,如國內的兩個機房,延遲較低。follow一寫多讀策略,備機房把寫流量轉入主機房,只有主機房會把WAL更新到KV存儲上。
廣域網容災部署,如新加坡和美國的兩臺機器,延遲較高。follow了mysql的思想,每次寫入在本地寫入成功后,會被轉化為binlog,再發送給其他單元;并通過hybrid logical clock保證各單元對于一條邊的操作順序一致性。

5. 離線在線數據流融合

導入存量數據、寫入在線數據,將二者集成在公司內部數據平臺進行離線數據分析,具體流程如圖。