【操作教程】SequoiaDB分散式存儲教程

【操作教程】SequoiaDB分散式存儲教程

來自專欄 SequoiaDB——NoSQL技術分享

1.各模式適用場景介紹

由於SequoiaDB對比其他的NoSQL有更多的方式將數據分布到多台伺服器上,所以下面筆者為閱讀者一一介紹每種分散式方式適合於哪種場景。

1.1 Hash 方式分布數據

在Hash分布方式中,由於是對集合中某個欄位的Hash值進行數據均勻,所以用戶未來在使用Hash分布時,ShardingKey 一定要選擇集合中欄位值比較隨機,並且Hash值比較均勻的欄位,這樣才能夠保證集合中的數據被均勻的分布在各個數據分區組上。

Hash分散式方式主要適合數據量比較大,並且集合中包含一個Hash值比較隨機的欄位。如果集合中並沒有Hash值比較隨機的欄位,但是集合的數據量又非常的巨大,用戶可以考慮使用SequoiaDB中的主鍵-「_id」作為ShardingKey。

1.2 Range 方式分布數據

Range 分布方式,主要適用用於集合數據量大,並且集合包含某個具有比較明確的數值範圍的欄位,例如時間欄位或者是業務類型欄位,來幫助用戶做集合的範圍切分。

1.3 Partition 方式分布數據

Partition 分布方式,和Range 分布方式的適用場景非常類似,都是要求集合數據量大,並且集合包含某個具有比較明確的數值範圍的欄位。

Partition分布方式,實際上並不能夠做到數據自動均勻分布到多個數據分區組,而是需要用戶在建立子表時,人工顯式指定此子表是被分配到哪個數據分區組上,然後再通過主表的attach 命令將子表按照某個欄位的範圍值掛載到主表上。

而Partition 分布方式與Range 分布方式最大的不同點,在於對數據的刪除上。

在Range 分布方式中,如果用戶需要對集合中某個過舊的時間範圍數據進行刪除,用戶需要調用remove 命令,真實地從資料庫磁碟中刪除這部分數據,這個耗時會比較久。

而在Partition 分布方式中,用戶同樣希望刪除某個過舊的時間範圍數據,用戶只需要調用dettach命令,將符合時間範圍的子表從主表中卸載下來,即可完成數據從集合中清除的目的,dettach命令的效率非常高,基本是秒級完成。然後用戶可以對過舊的子表執行truncate 命令,回收磁碟空間。

注意,集合truncate 命令的執行效率比集合remove 命令的執行效率要高出幾個量級。

1.4 多維分區

SequoiaDB的多維分區方式,它很好地結合了Hash分布方式和Partition 分布方式的優點,能夠讓集合中的數據以更小的顆粒度分布到資料庫多個數據分區組上。

多維分區分布方式,主要適合的場景是集合數據量特別巨大,集合中同時包含兩個關鍵的ShardingKey,一般為time和id兩個欄位,time欄位給主子表使用,id欄位給Hash 分布使用,並且用戶在使用在集合過程中,還可能會定期對部分數據進行空間回收。

在真實的客戶環境中,多維分區主要使用的場景為:銀行的歷史數據流水表,業務系統歷史日誌表等。

2.操作指南

作者在為下面各種數據分布方式做操作前,需要先在資料庫中一些準備操作。

連接到資料庫中

var db = new Sdb("localhost", 11810);

查看當前資料庫有多少數據分區組

var cursor = db.listReplicaGroups();while (cursor.next()){var obj = cursor.current().toObj();if (obj["GroupName"] != "SYSCoord" && obj["GroupName"] != "SYSCatalogGroup") {println (obj["GroupName"]);}} cursor.close ();

返回的結果為

group1group2group3group4

為了做Hash 切分方便,用戶可以先給資料庫建立Domain

db.createDomain ("domain1", ["group1", "group2"], {AutoSplit:true});db.createDomain ("domain2", ["group3", "group4"], {AutoSplit:true});

2.1 Hash 方式分布數據

建立一個集合空間,名為testcs_domain1,並且指定此集合空間是建立在domain1 上的

db.createCS("testcs_domain1", {Domain:"domain1"});

在testcs_domain1 集合空間上建立一個集合,名為testcl_hash,設置ShardingType = hash,ShardingKey = id

var CS = db.getCS("testcs_domain1");CS.createCL("testcl_hash", {ShardingType:"hash", ShardingKey:{id:1}});

檢查testcl_hash 集合在資料庫中的分布情況

db.snapshot(8, {Name:"testcs_domain1.testcl_hash"});{ "AutoSplit": true, "CataInfo": [ { "ID": 0, "GroupID": 1001, "GroupName": "group2", "LowBound": { "": 0 }, "UpBound": { "": 2048 } }, { "ID": 1, "GroupID": 1000, "GroupName": "group1", "LowBound": { "": 2048 }, "UpBound": { "": 4096 } } ], "EnsureShardingIndex": true, "InternalV": 3, "Name": "testcs_domain1.testcl_hash", "Partition": 4096, "ShardingKey": { "id": 1 }, "ShardingType": "hash", "Version": 2, "_id": { "$oid": "57c73fe8ed44740501b46dee" }}

用戶可以從snapshot 的輸出中看到,testcs_domain1.testcl_hash 被切分到group1 和group2 上,並且Hash桶是均勻分配的。

2.2 Range 方式分布數據

用戶在做Range 切分時,Domain 的功能就不能幫上忙了

為了讓本文檔的閱讀者更加清晰地了解Range 切分,作者在資料庫中新建一個集合空間,名為testcs_range

db.createCS("testcs_range");

建立一個名為testcl_range 的集合,ShardingKey = time, ShardingType = range,並且指定此集合在初始化時建立在group1 數據分區組上。

var CS = db.getCS("testcs_range");CS.createCL("testcl_range", {ShardingType:"range", ShardingKey:{time:1}, Group:"group1"});

對testcl_range 做範圍切分

CS.testcl_range.split ("group1", "group2", {time:"20160401"}, {time:"20160701"});CS.testcl_range.split ("group1", "group3", {time:"20160701"}, {time:"20161001"});CS.testcl_range.split ("group1", "group4", {time:"20161001"}, {time:"20170101"});

查看testcs_range.testcl_range 的切分情況

db.snapshot (8, {Name:"testcs_range.testcl_range"});{ "CataInfo": [ { "ID": 0, "GroupID": 1000, "GroupName": "group1", "LowBound": { "time": { "$minKey": 1 } }, "UpBound": { "time": "20160401" } }, { "ID": 1, "GroupID": 1001, "GroupName": "group2", "LowBound": { "time": "20160401" }, "UpBound": { "time": "20160701" } }, { "ID": 3, "GroupID": 1002, "GroupName": "group3", "LowBound": { "time": "20160701" }, "UpBound": { "time": "20161001" } }, { "ID": 4, "GroupID": 1003, "GroupName": "group4", "LowBound": { "time": "20161001" }, "UpBound": { "time": "20170101" } }, { "ID": 2, "GroupID": 1000, "GroupName": "group1", "LowBound": { "time": "20170101" }, "UpBound": { "time": { "$maxKey": 1 } } } ], "EnsureShardingIndex": true, "Name": "testcs_range.testcl_range", "ShardingKey": { "time": 1 }, "ShardingType": "range", "Version": 4, "_id": { "$oid": "57c754b6ed44740501b46e0e" }}

testcs_range.testcl_range 集合數據切分情況如下

group2 負責 time 範圍為 "20160401" ~ "20160701"

group3 負責 time 範圍為 "20160701" ~ "20161001"

group4 負責 time 範圍為 "20161001" ~ "20170101"

group1 負責 time 範圍為除 group2、group3、group4 範圍外的所有數據

用戶可以寫入兩條記錄去驗證一下

CS.testcl_range.insert({name:"test", id:1, time:"20160425"});CS.testcl_range.insert({name:"haha", id:2, time:"20160101"});

用戶可以通過快照查看每個數據分區組的記錄數

db.snapshot(4, {Name:"testcs_range.testcl_range"}, {"Details.Group.TotalRecords":null,"Details.GroupName":null});{ "Details": [ { "GroupName": "group1", "Group": [ { "TotalRecords": 1 }, { "TotalRecords": 1 }, { "TotalRecords": 1 } ] }, { "GroupName": "group2", "Group": [ { "TotalRecords": 1 }, { "TotalRecords": 1 }, { "TotalRecords": 1 } ] }, { "GroupName": "group3", "Group": [ { "TotalRecords": 0 }, { "TotalRecords": 0 }, { "TotalRecords": 0 } ] }, { "GroupName": "group4", "Group": [ { "TotalRecords": 0 }, { "TotalRecords": 0 }, { "TotalRecords": 0 } ] } ]}

由於本集群是一個數據分區組,包含3個數據副本,所以在快照信息中,一個group 會顯示3條記錄。

並且用戶從快照信息中,group1 包含1 條記錄,group2 包含1 條記錄,符合作者對testcs_range.testcl_range 的切分情況。

2.3 Parttion 方式分布數據

為了驗證使用主子表來做數據切分,作者在資料庫中新建一個名為 testcs_mainsub 的集合空間

db.createCS("testcs_mainsub");var CS = db.getCS("testcs_mainsub");

在 testcs_mainsub 集合空間上建立一個主表,ShardingType = range, ShardingKey = time, isMainCL = true

CS.createCL("testcl_main", {ShardingType:"range", ShardingKey:{time:1}, IsMainCL:true});

建立子表,並且在建立子表時,顯式指定每個子表被分配到哪個數據分區組上。

CS.createCL("testcl_sub1", {Group:"group1"});CS.createCL("testcl_sub2", {Group:"group2"});CS.createCL("testcl_sub3", {Group:"group3"});CS.createCL("testcl_sub4", {Group:"group4"});

將各個子表掛載到主表上

CS.testcl_main.attachCL ("testcs_mainsub.testcl_sub1", {LowBound:{time:"20160101"}, UpBound:{time:"20160401"}});CS.testcl_main.attachCL ("testcs_mainsub.testcl_sub2", {LowBound:{time:"20160401"}, UpBound:{time:"20160701"}});CS.testcl_main.attachCL ("testcs_mainsub.testcl_sub3", {LowBound:{time:"20160701"}, UpBound:{time:"20161001"}});CS.testcl_main.attachCL ("testcs_mainsub.testcl_sub4", {LowBound:{time:"20161001"}, UpBound:{time:"20170101"}});

用戶可以通過快照了解主表數據的切分情況

db.snapshot (8, {Name:"testcs_mainsub.testcl_main"});{ "CataInfo": [ { "ID": 1, "SubCLName": "testcs_mainsub.testcl_sub1", "LowBound": { "time": "20160101" }, "UpBound": { "time": "20160401" } }, { "ID": 2, "SubCLName": "testcs_mainsub.testcl_sub2", "LowBound": { "time": "20160401" }, "UpBound": { "time": "20160701" } }, { "ID": 3, "SubCLName": "testcs_mainsub.testcl_sub3", "LowBound": { "time": "20160701" }, "UpBound": { "time": "20161001" } }, { "ID": 4, "SubCLName": "testcs_mainsub.testcl_sub4", "LowBound": { "time": "20161001" }, "UpBound": { "time": "20170101" } } ], "EnsureShardingIndex": true, "IsMainCL": true, "Name": "testcs_mainsub.testcl_main", "ShardingKey": { "time": 1 }, "ShardingType": "range", "Version": 5, "_id": { "$oid": "57c75d35ed44740501b46e28" }}

目前 testcs_mainsub.testcl_main 的數據切分情況如下

testcl_sub1 屬於 group1,它負責的數據範圍為 20160101 ~ 20160401

testcl_sub2 屬於 group2,它負責的數據範圍為 20160401 ~ 20160701

testcl_sub3 屬於 group3,它負責的數據範圍為 20160701 ~ 20161001

testcl_sub4 屬於 group4,它負責的數據範圍為 20161001 ~ 20170101

用戶可以寫入兩條記錄來驗證一下數據的分布情況

CS.testcl_main.insert ({name:"test", id:1, time:"20160123"});CS.testcl_main.insert ({name:"haha", id:2, time:"20161207"});

用戶可以查看一下每個子表的數據情況

CS.testcl_sub1.count();CS.testcl_sub2.count();CS.testcl_sub3.count();CS.testcl_sub4.count();

圖6

數據分布情況符合主子表的切分範圍。

2.4 多維分區

為了驗證SequoiaDB 的多維分區功能,並且為了給閱讀者更好了解在多維分區和Domain功能的結合,作者在資料庫中新建兩個集合空間,分別為 testcs_doublepartition_domain1 和 testcs_doublepartition_domain2

db.createCS ("testcs_doublepartition_domain1", {Domain:"domain1"});var CS1 = db.getCS ("testcs_doublepartition_domain1");db.createCS ("testcs_doublepartition_domain2", {Domain:"domain2"});var CS2 = db.getCS ("testcs_doublepartition_domain2");

剛才作者已經在資料庫多維分區的技術原理也介紹了,SequoiaDB的多維分區,實際上就是在一個集合中,同時運用主子表和Hash分區兩個數據分區功能,從而使得集合能夠以更小的顆粒度做數據均衡。

在 testcs_mainsub 集合空間上建立一個主表,ShardingType = range, ShardingKey = time, isMainCL = true

var CS = db.getCS("testcs_mainsub");CS.createCL("testcl_main_doublepartition", {ShardingType:"range", ShardingKey:{time:1}, IsMainCL:true});

建立子表,並且在建立子表時,注意選擇集合空間,並且顯式指定ShardingType = hash 和 ShardingKey = id。

CS1.createCL("testcl_sub1", {ShardingType:"hash", ShardingKey:{id:1}});CS1.createCL("testcl_sub2", {ShardingType:"hash", ShardingKey:{id:1}});CS2.createCL("testcl_sub3", {ShardingType:"hash", ShardingKey:{id:1}});CS2.createCL("testcl_sub4", {ShardingType:"hash", ShardingKey:{id:1}});

將各個子表掛載到主表上

CS.testcl_main_doublepartition.attachCL ("testcs_doublepartition_domain1.testcl_sub1", {LowBound:{time:"20160101"}, UpBound:{time:"20160401"}});CS.testcl_main_doublepartition.attachCL ("testcs_doublepartition_domain1.testcl_sub2", {LowBound:{time:"20160401"}, UpBound:{time:"20160701"}});CS.testcl_main_doublepartition.attachCL ("testcs_doublepartition_domain2.testcl_sub3", {LowBound:{time:"20160701"}, UpBound:{time:"20161001"}});CS.testcl_main_doublepartition.attachCL ("testcs_doublepartition_domain2.testcl_sub4", {LowBound:{time:"20161001"}, UpBound:{time:"20170101"}});

用戶可以通過快照信息核對數據的切分情況

db.snapshot (8, {Name:"testcs_mainsub.testcl_main_doublepartition"});{ "CataInfo": [ { "ID": 1, "SubCLName": "testcs_doublepartition_domain1.testcl_sub1", "LowBound": { "time": "20160101" }, "UpBound": { "time": "20160401" } }, { "ID": 2, "SubCLName": "testcs_doublepartition_domain1.testcl_sub2", "LowBound": { "time": "20160401" }, "UpBound": { "time": "20160701" } }, { "ID": 3, "SubCLName": "testcs_doublepartition_domain2.testcl_sub3", "LowBound": { "time": "20160701" }, "UpBound": { "time": "20161001" } }, { "ID": 4, "SubCLName": "testcs_doublepartition_domain2.testcl_sub4", "LowBound": { "time": "20161001" }, "UpBound": { "time": "20170101" } } ], "EnsureShardingIndex": true, "IsMainCL": true, "Name": "testcs_mainsub.testcl_main_doublepartition", "ShardingKey": { "time": 1 }, "ShardingType": "range", "Version": 5, "_id": { "$oid": "57c80046ed44740501b46e43" }}testcs_doublepartition_domain1.testcl_sub1 屬於 group1,它負責的數據範圍為 20160101 ~ 20160401testcs_doublepartition_domain1.testcl_sub2 屬於 group2,它負責的數據範圍為 20160401 ~ 20160701testcs_doublepartition_domain2.testcl_sub3 屬於 group3,它負責的數據範圍為 20160701 ~ 20161001testcs_doublepartition_domain2.testcl_sub4 屬於 group4,它負責的數據範圍為 20161001 ~ 20170101

用戶也可以使用快照來確認一下各個子表是否被正確切分,例如查看集合 testcs_doublepartition_domain1.testcl_sub1 是否被分布到 domain1 上

db.snapshot (8, {Name:"testcs_doublepartition_domain1.testcl_sub1"});{ "AutoSplit": true, "CataInfo": [ { "ID": 0, "GroupID": 1000, "GroupName": "group1", "LowBound": { "": 0 }, "UpBound": { "": 2048 } }, { "ID": 1, "GroupID": 1001, "GroupName": "group2", "LowBound": { "": 2048 }, "UpBound": { "": 4096 } } ], "EnsureShardingIndex": true, "InternalV": 3, "MainCLName": "testcs_mainsub.testcl_main_doublepartition", "Name": "testcs_doublepartition_domain1.testcl_sub1", "Partition": 4096, "ShardingKey": { "id": 1 }, "ShardingType": "hash", "Version": 3, "_id": { "$oid": "57c8004eed44740501b46e46" }}

用戶從快照信息中了解到,testcs_doublepartition_domain1.testcl_sub1 集合被切分到group1 和 group2 上,滿足domain1 的數據分區組列表。

SequoiaDB巨杉資料庫2.6最新版下載

SequoiaDB巨杉資料庫技術博客

SequoiaDB巨杉資料庫社區

推薦閱讀:

大國崛起:資料庫領域的中國力量
PhxPaxos架構設計、實現分析
分散式時序資料庫 - LinDB
UCloud雲資料庫團隊誠招分散式資料庫研發

TAG:資料庫 | 分散式資料庫 | NoSQL |