
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 系統整體架構
2.2 存儲系統結構關系
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 邏輯分析
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
3.3 Create Checkpoint
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