天天看點

分布式圖資料庫 Nebula Graph 中的叢集快照實踐

分布式圖資料庫 Nebula Graph 中的叢集快照實踐

1 概述

1.1 需求背景

圖資料庫 Nebula Graph 在生産環境中将擁有龐大的資料量和高頻率的業務處理,在實際的運作中将不可避免的發生人為的、硬體或業務處理錯誤的問題,某些嚴重錯誤将導緻叢集無法正常運作或叢集中的資料失效。當叢集處于無法啟動或資料失效的狀态時,重新搭建叢集并重新倒入資料都将是一個繁瑣并耗時的工程。針對此問題,Nebula Graph 提供了叢集 snapshot 的建立功能。

Snapshot 功能需要預先提供叢集在某個時間點 snapshot 的建立功能,以備發生災難性問題時用曆史 snapshot 便捷地将叢集恢複到一個可用狀态。

1.2 術語

本文主要會用到以下術語:

  • StorageEngine:Nebula Graph 的最小實體存儲單元,目前支援 RocksDB 和 HBase,在本文中隻針對 RocksDB。
  • Partition:Nebula Graph 的最小邏輯存儲單元,一個 StorageEngine 可包含多個 Partition。Partition 分為 leader 和 follower 的角色,Raftex 保證了 leader 和 follower 之間的資料一緻性。
  • GraphSpace:每個 GraphSpace 是一個獨立的業務 Graph  單元,每個 GraphSpace 有其獨立的 tag 和 edge 集合。一個 Nebula Graph 叢集中可包含多個 GraphShpace。
  • checkpoint:針對 StorageEngine 的一個時間點上的快照,checkpoint 可以作為全量備份的一個 backup 使用。checkpoint files是 sst files 的一個硬連接配接。
  • snapshot:本文中的 snapshot 是指 Nebula Graph 叢集的某個時間點的快照,即叢集中所有 StorageEngine 的 checkpoint 的集合。通過 snapshot 可以将叢集恢複到某個 snapshot 建立時的狀态。
  • wal:Write-Ahead Logging ,用 raftex 保證 leader 和 follower 的一緻性。

2 系統構架

2.1 系統整體架構

分布式圖資料庫 Nebula Graph 中的叢集快照實踐

2.2 存儲系統結構關系

分布式圖資料庫 Nebula Graph 中的叢集快照實踐

2.3 存儲系統實體檔案結構

[bright2star@hp-server storage]$ tree
.
└── nebula
    └── 1
        ├── checkpoints
        │   ├── SNAPSHOT_2019_12_04_10_54_42
        │   │   ├── data
        │   │   │   ├── 000006.sst
        │   │   │   ├── 000008.sst
        │   │   │   ├── CURRENT
        │   │   │   ├── MANIFEST-000007
        │   │   │   └── OPTIONS-000005
        │   │   └── wal
        │   │       ├── 1
        │   │       │   └── 0000000000000000233.wal
        │   │       ├── 2
        │   │       │   └── 0000000000000000233.wal
        │   │       ├── 3
        │   │       │   └── 0000000000000000233.wal
        │   │       ├── 4
        │   │       │   └── 0000000000000000233.wal
        │   │       ├── 5
        │   │       │   └── 0000000000000000233.wal
        │   │       ├── 6
        │   │       │   └── 0000000000000000233.wal
        │   │       ├── 7
        │   │       │   └── 0000000000000000233.wal
        │   │       ├── 8
        │   │       │   └── 0000000000000000233.wal
        │   │       └── 9
        │   │           └── 0000000000000000233.wal
        │   └── SNAPSHOT_2019_12_04_10_54_44
        │       ├── data
        │       │   ├── 000006.sst
        │       │   ├── 000008.sst
        │       │   ├── 000009.sst
        │       │   ├── CURRENT
        │       │   ├── MANIFEST-000007
        │       │   └── OPTIONS-000005
        │       └── wal
        │           ├── 1
        │           │   └── 0000000000000000236.wal
        │           ├── 2
        │           │   └── 0000000000000000236.wal
        │           ├── 3
        │           │   └── 0000000000000000236.wal
        │           ├── 4
        │           │   └── 0000000000000000236.wal
        │           ├── 5
        │           │   └── 0000000000000000236.wal
        │           ├── 6
        │           │   └── 0000000000000000236.wal
        │           ├── 7
        │           │   └── 0000000000000000236.wal
        │           ├── 8
        │           │   └── 0000000000000000236.wal
        │           └── 9
        │               └── 0000000000000000236.wal
        ├── data           

3 處理邏輯分析

3.1 邏輯分析

分布式圖資料庫 Nebula Graph 中的叢集快照實踐

Create snapshot

  由

client api

  或

console

  觸發,

graph server

  對

create snapshot

  的 AST 進行解析,然後通過

meta client

  将建立請求發送到

meta server

 。

meta server

  接到請求後,首先會擷取所有的

active host

 ,并建立

adminClient

  所需的

request

 。通過

adminClient

  将建立請求發送到每個

StorageEngine

 ,

StorageEngine

收到 create 請求後,會周遊指定 space 的全部 StorageEngine,并建立

checkpoint

 ,随後對 

StorageEngine

中的全部

partition

  的 wal 做 hardlink。在建立 checkpoint 和 wal hardlink 時,因為已經提前向所有 leader partition 發送了 write blocking 請求,是以此時資料庫是隻讀狀态的。

因為 snapshot 的名稱是由系統的 timestamp 自動生成,是以不必擔心 snapshot 的重名問題。如果建立了不必要的 snapshot,可以通過 drop snapshot 指令删除已建立的 snapshot。

3.2 Create Snapshot

分布式圖資料庫 Nebula Graph 中的叢集快照實踐

3.3 Create Checkpoint

分布式圖資料庫 Nebula Graph 中的叢集快照實踐

4 關鍵代碼實作

4.1 Create Snapshot

folly::Future<Status> AdminClient::createSnapshot(GraphSpaceID spaceId, const std::string& name) {
    // 擷取所有storage engine的host
    auto allHosts = ActiveHostsMan::getActiveHosts(kv_);
    storage::cpp2::CreateCPRequest req;
    
    // 指定spaceId,目前是對所有space做checkpoint,list spaces 工作已在調用函數中執行。
    req.set_space_id(spaceId);
    
    // 指定 snapshot name,已有meta server根據時間戳産生。
    // 例如:SNAPSHOT_2019_12_04_10_54_44
    req.set_name(name);
    folly::Promise<Status> pro;
    auto f = pro.getFuture();
    
    // 通過getResponse接口發送請求到所有的storage engine.
    getResponse(allHosts, 0, std::move(req), [](auto client, auto request) {
        return client->future_createCheckpoint(request);
    }, 0, std::move(pro), 1 /*The snapshot operation only needs to be retried twice*/);
    return f;
}           

4.2 Create Checkpoint

ResultCode NebulaStore::createCheckpoint(GraphSpaceID spaceId, const std::string& name) {
    auto spaceRet = space(spaceId);
    if (!ok(spaceRet)) {
        return error(spaceRet);
    }
    auto space = nebula::value(spaceRet);
    
    // 周遊屬于本space中的所有StorageEngine
    for (auto& engine : space->engines_) {
        
        // 首先對StorageEngine做checkpoint
        auto code = engine->createCheckpoint(name);
        if (code != ResultCode::SUCCEEDED) {
            return code;
        }
        
        // 然後對本StorageEngine中的所有partition的last wal做hardlink
        auto parts = engine->allParts();
        for (auto& part : parts) {
            auto ret = this->part(spaceId, part);
            if (!ok(ret)) {
                LOG(ERROR) << "Part not found. space : " << spaceId << " Part : " << part;
                return error(ret);
            }
            auto walPath = folly::stringPrintf("%s/checkpoints/%s/wal/%d",
                                                      engine->getDataRoot(), name.c_str(), part);
            auto p = nebula::value(ret);
            if (!p->linkCurrentWAL(walPath.data())) {
                return ResultCode::ERR_CHECKPOINT_ERROR;
            }
        }
    }
    return ResultCode::SUCCEEDED;
}           

5 使用者使用幫助

5.1 CREATE SNAPSHOT

CREATE SNAPSHOT

  即對整個叢集建立目前時間點的快照,snapshot 名稱由 meta server 的 timestamp 組成。

在建立過程中可能會建立失敗,目前版本不支援建立失敗的垃圾回收的自動功能,後續将計劃在 metaServer 中開發 cluster checker 的功能,将通過異步線程檢查叢集狀态,并自動回收 snapshot 建立失敗的垃圾檔案。

目前版本如果 snapshot 建立失敗,必須通過

DROP SNAPSHOT

 指令清除無效的 snapshot。

目前版本不支援對指定的 space 做 snapshot,當執行 CREATE SNAPSHOT 後,将對叢集中的所有 space 建立快照。

CREATE SNAPSHOT 文法:

CREATE SNAPSHOT           

以下為筆者建立 3 個 snapshot 的例子:

([email protected]) [default_space]> create snapshot;
Execution succeeded (Time spent: 28211/28838 us)

([email protected]) [default_space]> create snapshot;
Execution succeeded (Time spent: 22892/23923 us)

([email protected]) [default_space]> create snapshot;
Execution succeeded (Time spent: 18575/19168 us)           

我們用 5.3 提及的

SHOW SNAPSHOTS

 指令看下現在有的快照

([email protected]) [default_space]> show snapshots;
===========================================================
| Name                         | Status | Hosts           |
===========================================================
| SNAPSHOT_2019_12_04_10_54_36 | VALID  | 127.0.0.1:77833 |
-----------------------------------------------------------
| SNAPSHOT_2019_12_04_10_54_42 | VALID  | 127.0.0.1:77833 |
-----------------------------------------------------------
| SNAPSHOT_2019_12_04_10_54_44 | VALID  | 127.0.0.1:77833 |
-----------------------------------------------------------
Got 3 rows (Time spent: 907/1495 us)           

從上

SNAPSHOT_2019_12_04_10_54_36

  可見 snapshot 名同 timestamp 有關。

5.2 DROP SNAPSHOT

DROP SNAPSHOT

 即删除指定名稱的 snapshot,可以通過

SHOW SNAPSHOTS

 指令擷取 snapshot 的名稱,DROP SNAPSHOT 既可以删除有效的 snapshot,也可以删除建立失敗的 snapshot。

文法:

DROP SNAPSHOT name           

筆者删除了 5.1 成功建立的 snapshot 

SNAPSHOT_2019_12_04_10_54_36

,并用

SHOW SNAPSHOTS

 指令檢視現有的 snapshot。

([email protected]) [default_space]> drop snapshot SNAPSHOT_2019_12_04_10_54_36;
Execution succeeded (Time spent: 6188/7348 us)

([email protected]) [default_space]> show snapshots;
===========================================================
| Name                         | Status | Hosts           |
===========================================================
| SNAPSHOT_2019_12_04_10_54_42 | VALID  | 127.0.0.1:77833 |
-----------------------------------------------------------
| SNAPSHOT_2019_12_04_10_54_44 | VALID  | 127.0.0.1:77833 |
-----------------------------------------------------------
Got 2 rows (Time spent: 1097/1721 us)           

5.3 SHOW SNAPSHOTS

SHOW SNAPSHOTS

 可檢視叢集中所有的 snapshot,可以通過

SHOW SNAPSHOTS

 指令檢視其狀态(VALID 或 INVALID)、名稱、和建立 snapshot 時所有 storage Server 的 ip 位址。

SHOW SNAPSHOTS           

以下為一個小示例:

([email protected]) [default_space]> show snapshots;
===========================================================
| Name                         | Status | Hosts           |
===========================================================
| SNAPSHOT_2019_12_04_10_54_36 | VALID  | 127.0.0.1:77833 |
-----------------------------------------------------------
| SNAPSHOT_2019_12_04_10_54_42 | VALID  | 127.0.0.1:77833 |
-----------------------------------------------------------
| SNAPSHOT_2019_12_04_10_54_44 | VALID  | 127.0.0.1:77833 |
-----------------------------------------------------------
Got 3 rows (Time spent: 907/1495 us)           

6 注意事項

  • 當系統結構發生變化後,最好立刻 create snapshot,例如 add host、drop host、create space、drop space、balance 等。
  • 目前版本暫未提供使用者指定 snapshot 路徑的功能,snapshot 将預設建立在 data_path/nebula 目錄下。
  • 目前版本暫未提供 snapshot 的恢複功能,需要使用者根據實際的生産環境編寫 shell 腳本實作。實作邏輯也比較簡單,拷貝各 engineServer 的 snapshot 到指定的檔案夾下,并将此檔案夾設定為 data_path,啟動叢集即可。

7 附錄

最後,附上 Nebula Graph GitHub 位址:

https://github.com/vesoft-inc/nebula

如果你在使用 Nebula Graph 過程中遇到任何問題,歡迎 GitHub 聯系我們或者加入微信交流群,請聯系微信号:NebulaGraphbot 

分布式圖資料庫 Nebula Graph 中的叢集快照實踐