Sharding představuje proces, během něhož se rozsáhlé datové soubory rozdělují na menší segmenty, které se následně distribuují mezi různé instance MongoDB v rámci distribuovaného prostředí.
Co Znamená Sharding?
Sharding v MongoDB umožňuje vytvářet škálovatelná řešení pro ukládání rozsáhlých objemů dat. Místo jediného serveru se data ukládají na více serverech, což zvyšuje celkovou kapacitu a výkon systému.
Ukládání neustále rostoucího množství dat na jeden počítač je v praxi neudržitelné. Dotazování na takto obrovské databáze může vést k vysokému zatížení serveru a nedostatečné propustnosti pro operace čtení a zápisu.
Existují dva hlavní přístupy k škálování, které umožňují zpracování rostoucího objemu dat:
Vertikální škálování spočívá ve zvýšení výpočetní síly jednoho serveru. To se realizuje pomocí výkonnějších procesorů, rozšíření paměti RAM, nebo přidáním diskového prostoru. Nicméně, vertikální škálování má svá omezení a v reálných situacích nemusí být vždy efektivní.
Horizontální škálování se zaměřuje na přidávání dalších serverů a rozkládání zátěže mezi ně. Každý server zpracovává pouze část celkového datového objemu, což vede k vyšší efektivitě a hospodárnosti. Nicméně, údržba rozsáhlé infrastruktury s velkým počtem serverů může být náročná.
MongoDB sharding využívá techniku horizontálního škálování.
Součásti Shardingu
K implementaci shardingu v MongoDB je zapotřebí několik klíčových komponent:
Shard je instance MongoDB, která je zodpovědná za ukládání a správu podmnožiny celkových dat. Shardy jsou typicky implementovány jako replikační sady pro zajištění vysoké dostupnosti.
Mongos je instance MongoDB, která funguje jako most mezi klientskou aplikací a shardovaným clusterem. Přijímá dotazy od klientů a směruje je na příslušné shardy.
Konfigurační server je instance MongoDB, která uchovává metadata o clusteru, včetně informací o konfiguraci shardů. Je klíčový pro správnou funkci shardingu a musí být nasazen jako replikační sada.
Architektura Shardingu
Cluster MongoDB pro sharding se skládá z několika replikačních sad.
Každá replikační sada obsahuje minimálně tři instance mongo. Shardovaný cluster může obsahovat mnoho fragmentů, přičemž každý fragment funguje jako replikační sada. Aplikace komunikují s Mongos, který následně komunikuje se shardy. Z tohoto důvodu aplikace nikdy nekomunikují přímo s uzly shardů. Mongos distribuuje data mezi uzly shardů na základě sharding klíče.
Implementace Shardingu
Pro implementaci shardingu postupujte podle následujících kroků:
Krok 1
- Spusťte konfigurační server jako replikační sadu a povolte replikaci mezi jeho členy.
mongod --configsvr --port 27019 --replSet rs0 --dbpath C:datadata1 --bind_ip localhost
mongod --configsvr --port 27018 --replSet rs0 --dbpath C:datadata2 --bind_ip localhost
mongod --configsvr --port 27017 --replSet rs0 --dbpath C:datadata3 --bind_ip localhost
Krok 2
- Inicializujte replikační sadu na jednom z konfiguračních serverů.
rs.initiate( { _id : "rs0", configsvr: true, members: [ { _id: 0, host: "IP:27017" }, { _id: 1, host: "IP:27018" }, { _id: 2, host: "IP:27019" } ] })
rs.initiate( { _id : "rs0", configsvr: true, members: [ { _id: 0, host: "IP:27017" }, { _id: 1, host: "IP:27018" }, { _id: 2, host: "IP:27019" } ] })
{
"ok" : 1,
"$gleStats" : {
"lastOpTime" : Timestamp(1593569257, 1),
"electionId" : ObjectId("000000000000000000000000")
},
"lastCommittedOpTime" : Timestamp(0, 0),
"$clusterTime" : {
"clusterTime" : Timestamp(1593569257, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1593569257, 1)
}
Krok 3
- Spusťte sharding servery jako replikační sady a povolte replikaci.
mongod --shardsvr --port 27020 --replSet rs1 --dbpath C:data4 --bind_ip localhost
mongod --shardsvr --port 27021 --replSet rs1 --dbpath C:data5 --bind_ip localhost
mongod --shardsvr --port 27022 --replSet rs1 --dbpath C:data6 --bind_ip localhost
MongoDB inicializuje první sharding server jako primární. Pokud je potřeba změnit primární sharding server, použijte metodu movePrimary.
Krok 4
- Inicializujte replikační sadu na jednom z shard serverů.
rs.initiate( { _id : "rs0", members: [ { _id: 0, host: "IP:27020" }, { _id: 1, host: "IP:27021" }, { _id: 2, host: "IP:27022" } ] })
rs.initiate( { _id : "rs0", members: [ { _id: 0, host: "IP:27020" }, { _id: 1, host: "IP:27021" }, { _id: 2, host: "IP:27022" } ] })
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1593569748, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1593569748, 1)
}
Krok 5
- Spusťte instanci Mongos pro shardovaný cluster.
mongos --port 40000 --configdb rs0/localhost:27019,localhost:27018,localhost:27017
Krok 6
- Připojte se k serveru Mongos.
mongo --port 40000
- Nyní přidejte sharding servery.
sh.addShard( "rs1/localhost:27020,localhost:27021,localhost:27022")
sh.addShard( "rs1/localhost:27020,localhost:27021,localhost:27022")
{
"shardAdded" : "rs1",
"ok" : 1,
"operationTime" : Timestamp(1593570212, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1593570212, 2),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
Krok 7
- V mongo shellu povolte sharding pro databázi a kolekci.
- Povolte sharding pro databázi.
sh.enableSharding("geekFlareDB")
sh.enableSharding("geekFlareDB")
{
"ok" : 1,
"operationTime" : Timestamp(1591630612, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1591630612, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
Krok 8
- Pro shardování kolekce je vyžadován klíč (podrobněji popsaný níže).
Syntaxe: sh.shardCollection("dbName.collectionName", { "key" : 1 } )
sh.shardCollection("geekFlareDB.geekFlareCollection", { "key" : 1 } )
{
"collectionsharded" : "geekFlareDB.geekFlareCollection",
"collectionUUID" : UUID("0d024925-e46c-472a-bf1a-13a8967e97c1"),
"ok" : 1,
"operationTime" : Timestamp(1593570389, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1593570389, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
V případě, že kolekce neexistuje, vytvořte ji následovně:
db.createCollection("geekFlareCollection")
{
"ok" : 1,
"operationTime" : Timestamp(1593570344, 4),
"$clusterTime" : {
"clusterTime" : Timestamp(1593570344, 5),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
Krok 9
Vložte data do kolekce. Logy MongoDB začnou indikovat, že balancer pracuje a snaží se vyvážit data mezi shardy.
Krok 10
Posledním krokem je ověření stavu shardingu. Stav lze zkontrolovat pomocí následujícího příkazu spuštěného na uzlu Mongos.
Stav Shardingu
Zkontrolujte stav shardingu spuštěním níže uvedeného příkazu na směrovacím uzlu mongo.
sh.status()
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("5ede66c22c3262378c706d21")
}
shards:
{ "_id" : "rs1", "host" : "rs1/localhost:27020,localhost:27021,localhost:27022", "state" : 1 }
active mongoses:
"4.2.7" : 1
autosplit:
Currently enabled: yes
balancer:
Currently enabled: yes
Currently running: no
Failed balancer rounds in last 5 attempts: 5
Last reported error: Could not find host matching read preference { mode: "primary" } for set rs1
Time of Reported error: Tue Jun 09 2020 15:25:03 GMT+0530 (India Standard Time)
Migration Results for the last 24 hours:
No recent migrations
databases:
{ "_id" : "config", "primary" : "config", "partitioned" : true }
config.system.sessions
shard key: { "_id" : 1 }
unique: false
balancing: true
chunks:
rs1 1024
too many chunks to print, use verbose if you want to force print
{ "_id" : "geekFlareDB", "primary" : "rs1", "partitioned" : true, "version" : { "uuid" : UUID("a770da01-1900-401e-9f34-35ce595a5d54"), "lastMod" : 1 } }
geekFlareDB.geekFlareCol
shard key: { "key" : 1 }
unique: false
balancing: true
chunks:
rs1 1
{ "key" : { "$minKey" : 1 } } -->> { "key" : { "$maxKey" : 1 } } on : rs1 Timestamp(1, 0)
geekFlareDB.geekFlareCollection
shard key: { "product" : 1 }
unique: false
balancing: true
chunks:
rs1 1
{ "product" : { "$minKey" : 1 } } -->> { "product" : { "$maxKey" : 1 } } on : rs1 Timestamp(1, 0)
{ "_id" : "test", "primary" : "rs1", "partitioned" : false, "version" : { "uuid" : UUID("fbc00f03-b5b5-4d13-9d09-259d7fdb7289"), "lastMod" : 1 } }
mongos>
Distribuce Dat
Mongos směrovač rozděluje zátěž mezi shardy na základě shard klíče a zajišťuje rovnoměrnou distribuci dat. Pokud dojde k nerovnoměrnému rozdělení dat, aktivuje se balancer.
Klíčové komponenty pro distribuci dat:
- Balancer vyvažuje podmnožiny dat mezi shard uzly. Balancer se spustí, jakmile Mongos začne distribuovat zátěž. Po spuštění balancer provádí rovnoměrnější rozložení dat. Chcete-li zkontrolovat stav balanceru, spusťte
sh.status()
nebosh.getBalancerState()
nebosh.isBalancerRunning()
.
mongos> sh.isBalancerRunning()
true
mongos>
NEBO
mongos> sh.getBalancerState()
true
mongos>
Po vložení dat můžeme zaznamenat aktivitu démona Mongos, která indikuje přesouvání chunků pro konkrétní shardy, což znamená, že se balancer snaží vyvážit data mezi shardy. Běh balanceru může vést k problémům s výkonem, proto se doporučuje spouštět jej v určitém časovém okně.
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("5efbeff98a8bbb2d27231674")
}
shards:
{ "_id" : "rs1", "host" : "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022", "state" : 1 }
{ "_id" : "rs2", "host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025", "state" : 1 }
active mongoses:
"4.2.7" : 1
autosplit:
Currently enabled: yes
balancer:
Currently enabled: yes
Currently running: yes
Failed balancer rounds in last 5 attempts: 5
Last reported error: Could not find host matching read preference { mode: "primary" } for set rs2
Time of Reported error: Wed Jul 01 2020 14:39:59 GMT+0530 (India Standard Time)
Migration Results for the last 24 hours:
1024 : Success
databases:
{ "_id" : "config", "primary" : "config", "partitioned" : true }
config.system.sessions
shard key: { "_id" : 1 }
unique: false
balancing: true
chunks:
rs2 1024
too many chunks to print, use verbose if you want to force print
{ "_id" : "geekFlareDB", "primary" : "rs2", "partitioned" : true, "version" : { "uuid" : UUID("a8b8dc5c-85b0-4481-bda1-00e53f6f35cd"), "lastMod" : 1 } }
geekFlareDB.geekFlareCollection
shard key: { "key" : 1 }
unique: false
balancing: true
chunks:
rs2 1
{ "key" : { "$minKey" : 1 } } -->> { "key" : { "$maxKey" : 1 } } on : rs2 Timestamp(1, 0)
{ "_id" : "test", "primary" : "rs2", "partitioned" : false, "version" : { "uuid" : UUID("a28d7504-1596-460e-9e09-0bdc6450028f"), "lastMod" : 1 } }
mongos>
- Shard klíč určuje, jak jsou dokumenty rozloženy mezi shardy. Může se jednat o indexované pole nebo složené pole, které musí být přítomno ve všech dokumentech v shardované kolekci. Data se dělí do chunků, a každý chunk je na základě rozsahu datového klíče přidělen k určitému shardu. Router na základě rozsahu dotazu rozhoduje, do kterého shardu bude chunk uložen.
Při výběru shard klíče je třeba zvážit pět vlastností:
- Kardinalita
- Rozdělení zápisů
- Rozdělení čtení
- Cílení čtení
- Lokalita čtení
Ideální klíč umožňuje MongoDB rovnoměrně rozdělit zatížení mezi všechny shardy. Výběr vhodného klíče je zásadní pro výkon shardovaného clusteru.
Odebrání Uzlu Shardu
Před odstraněním shardu z clusteru je nutné zajistit bezpečnou migraci dat na zbývající shardy. MongoDB automaticky přesune data do jiných uzlů shardu, než je požadovaný uzel shardu odstraněn.
Pro odstranění shardu postupujte podle těchto kroků:
Krok 1
Nejprve je potřeba zjistit jméno hostitele shardu, který má být odstraněn. Následující příkaz zobrazí seznam všech shardů v clusteru spolu s jejich stavem.
db.adminCommand( { listShards: 1 } )
mongos> db.adminCommand( { listShards: 1 } )
{
"shards" : [
{
"_id" : "rs1",
"host" : "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022",
"state" : 1
},
{
"_id" : "rs2",
"host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025",
"state" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1593572866, 15),
"$clusterTime" : {
"clusterTime" : Timestamp(1593572866, 15),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
Krok 2
Odeberte požadovaný shard z clusteru pomocí následujícího příkazu. Po jeho spuštění se balancer postará o odstranění chunků z vyprazdňovaného shardu a vyrovná rozdělení zbývajících chunků mezi ostatní shardy.
db.adminCommand( { removeShard: "shardedReplicaNodes" } )
mongos> db.adminCommand( { removeShard: "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022" } )
{
"msg" : "draining started successfully",
"state" : "started",
"shard" : "rs1",
"note" : "you need to drop or movePrimary these databases",
"dbsToMove" : [ ],
"ok" : 1,
"operationTime" : Timestamp(1593572385, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1593572385, 2),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
Krok 3
Zkontrolujte stav vyprazdňování shardu opakovaným spuštěním stejného příkazu.
db.adminCommand( { removeShard: "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022" } )
Je nutné počkat, dokud se proces vyprazdňování dat nedokončí. Pole msg
a state
budou indikovat, zda bylo vyprazdňování dat dokončeno, nebo ne:
"msg" : "draining ongoing",
"state" : "ongoing",
Stav můžete také zkontrolovat pomocí příkazu sh.status()
. Odstraněný uzel se ve výstupu nezobrazí, pokud ovšem neprobíhá vyprazdňování, v tom případě bude stav vyprazdňování pro daný uzel uveden jako true.
Krok 4
Opakovaně kontrolujte stav vyprazdňování stejným výše uvedeným příkazem, dokud není požadovaný shard zcela odstraněn.
Jakmile bude operace dokončena, výstup příkazu bude obsahovat zprávu a stav completed
.
"msg" : "removeshard completed successfully",
"state" : "completed",
"shard" : "rs1",
"ok" : 1,
Krok 5
Nakonec zkontrolujte zbývající shardy v clusteru. Pro kontrolu stavu použijte sh.status()
nebo db.adminCommand( { listShards: 1 } )
.
mongos> db.adminCommand( { listShards: 1 } )
{
"shards" : [
{
"_id" : "rs2",
"host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025",
"state" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1593575215, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1593575215, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
Zde je vidět, že odstraněný shard již není v seznamu přítomen.
Výhody Shardingu oproti Replikaci
- Při replikaci primární uzel zpracovává veškeré operace zápisu, zatímco sekundární servery udržují záložní kopie nebo obsluhují operace pouze pro čtení. V shardovaném prostředí se zátěž distribuuje mezi několik serverů.
- Jedna replikační sada je omezena na 12 uzlů, avšak počet shardů není omezen.
- Replikace vyžaduje vysoce výkonný hardware nebo vertikální škálování pro zpracování velkých datových sad, což je nákladné. Sharding nabízí efektivnější způsob, jak zvládat velké objemy dat pomocí přidávání serverů.
- Při replikaci lze zvýšit výkon čtení přidáním sekundárních serverů, zatímco sharding zvyšuje jak výkon čtení, tak i zápisu přidáním dalších shard uzlů.
Omezení Shardingu
- Shardovaný cluster nepodporuje jedinečné indexování napříč shardy, pokud jedinečný index není opatřen předponou kompletním shard klíčem.
- Všechny aktualizační operace na shardované kolekci musí obsahovat shard klíč nebo pole
_id
v dotazu. - Kolekce lze rozdělit, pokud jejich velikost nepřekročí stanovený práh. Tento práh je odvozen od průměrné velikosti všech shard klíčů a nakonfigurované velikosti chunků.
- Sharding má provozní limity pro maximální velikost kolekce nebo počet rozdělení.
- Nesprávný výběr shard klíče negativně ovlivňuje výkon.
Závěr
MongoDB nabízí zabudovaný sharding pro implementaci velkých databází bez ztráty výkonu. Doufám, že uvedené informace vám pomohou s nastavením shardingu MongoDB. Pro další studium doporučuji se seznámit s některými běžně používanými příkazy MongoDB.