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

SequoiaDB發表於2016-11-09

1.各模式適用場景介紹

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

1.1Hash 方式分佈資料

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

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

1.2Range 方式分佈資料

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

1.3Partition 方式分佈資料

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 ();
返回的結果為
group1
group2
group3
group4

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

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

2.1Hash 方式分佈資料

建立一個集合空間,名為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.2Range 方式分佈資料

使用者在做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.3Parttion 方式分佈資料

為了驗證使用主子表來做資料切分,作者在資料庫中新建一個名為 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();

enter image description here

圖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 ~ 20160401
testcs_doublepartition_domain1.testcl_sub2 屬於 group2,它負責的資料範圍為 20160401 ~ 20160701
testcs_doublepartition_domain2.testcl_sub3 屬於 group3,它負責的資料範圍為 20160701 ~ 20161001
testcs_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巨杉資料庫社群

相關文章