Deljenje (sharding) predstavlja proces razdvajanja obimnih skupova podataka na manje, upravljivije delove. Ovi manji skupovi se raspoređuju između različitih instanci MongoDB-a unutar distribuiranog okruženja.
Šta je to deljenje (sharding)?
MongoDB deljenje omogućava skalabilno rešenje za čuvanje velike količine podataka. Umesto da se oslanjamo na jedan server, podaci se raspoređuju među više servera.
U praksi, čuvanje eksponencijalno rastućih podataka na jednoj mašini nije održivo. Upiti nad ogromnom količinom podataka pohranjenom na jednom serveru mogu dovesti do preopterećenja resursa i potencijalno neefikasne propusnosti za čitanje i pisanje.
Postoje dva osnovna načina skaliranja sistema za preuzimanje podataka:
Vertikalno skaliranje se postiže unapređivanjem performansi jednog servera. To uključuje dodavanje jačih procesora, nadogradnju RAM-a ili povećanje prostora na disku. Međutim, primena vertikalnog skaliranja može imati ograničenja u praksi, s obzirom na trenutne tehnologije i hardverske konfiguracije.
Horizontalno skaliranje se zasniva na dodavanju novih servera i raspodeli opterećenja među njima. Svaki server upravlja samo delom ukupnog skupa podataka. Ovo obezbeđuje bolju efikasnost i isplativo rešenje u poređenju sa ulaganjem u vrhunski hardver. Ipak, zahteva dodatno održavanje složene infrastrukture sa većim brojem servera.
MongoDB deljenje koristi tehniku horizontalnog skaliranja.
Komponente deljenja
Za implementaciju deljenja u MongoDB-u, potrebne su sledeće komponente:
Šard (Shard) je MongoDB instanca koja obrađuje podskup originalnih podataka. Šardovi se moraju konfigurisati unutar replika seta.
Mongos je MongoDB instanca koja služi kao interfejs između klijentske aplikacije i klastera koji koristi deljenje. Deluje kao ruter upita ka odgovarajućim šardovima.
Config Server je MongoDB instanca koja čuva informacije o metapodacima i detaljima konfiguracije klastera. MongoDB zahteva da se konfiguracioni server implementira kao replika set.
Arhitektura deljenja
MongoDB klaster se sastoji od više replika setova.
Svaki replika set obuhvata najmanje 3 ili više MongoDB instanci. Deljeni klaster može imati više instanci šardova, a svaka instanca šarda radi unutar replika seta. Aplikacija komunicira sa Mongosom, koji dalje prosleđuje zahteve šardovima. Dakle, aplikacije nikada ne komuniciraju direktno sa čvorovima šardova. Ruter upita distribuira podskupove podataka između čvorova šarda na osnovu ključa šarda.
Implementacija deljenja
Sledite korake navedene u nastavku za implementaciju deljenja:
Korak 1
- Pokrenite konfiguracioni server unutar replika seta i omogućite replikaciju među njima.
mongod --configsvr --port 27019 --replSet rs0 --dbpath C:data\data1 --bind_ip localhost
mongod --configsvr --port 27018 --replSet rs0 --dbpath C:data\data2 --bind_ip localhost
mongod --configsvr --port 27017 --replSet rs0 --dbpath C:data\data3 --bind_ip localhost
Korak 2
- Inicijalizujte replika set na jednom od konfiguracionih servera.
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)
}
Korak 3
- Pokrenite servere za šardiranje unutar replika seta i omogućite replikaciju među njima.
mongod --shardsvr --port 27020 --replSet rs1 --dbpath C:data\data4 --bind_ip localhost
mongod --shardsvr --port 27021 --replSet rs1 --dbpath C:data\data5 --bind_ip localhost
mongod --shardsvr --port 27022 --replSet rs1 --dbpath C:data\data6 --bind_ip localhost
MongoDB inicijalizuje prvi server za deljenje kao primarni, a za premeštanje primarnog servera za šardiranje koristi se movePrimary metoda.
Korak 4
- Inicijalizujte replika set na jednom od servera za deljenje.
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)
}
Korak 5
- Pokrenite mongos za klaster sa deljenjem.
mongos --port 40000 --configdb rs0/localhost:27019,localhost:27018,localhost:27017
Korak 6
- Povežite se na mongos ruter.
mongo --port 40000
- Sada dodajte servere za šardiranje.
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)
}
}
}
Korak 7
- U MongoDB shell-u, omogućite deljenje za bazu podataka i kolekcije.
- Omogućite deljenje za bazu podataka.
sh.enableSharding("geekFlareDB")
{
"ok" : 1,
"operationTime" : Timestamp(1591630612, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1591630612, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
Korak 8
- Potrebno je definisati ključ šarda za kolekciju (više o tome u nastavku teksta).
Sintaksa: 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)
}
}
}
Napomena: Ukoliko kolekcija ne postoji, kreirajte je na sledeći način.
db.createCollection("geekFlareCollection")
{
"ok" : 1,
"operationTime" : Timestamp(1593570344, 4),
"$clusterTime" : {
"clusterTime" : Timestamp(1593570344, 5),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
Korak 9
Ubacite podatke u kolekciju. MongoDB logovi će početi da rastu, što ukazuje da je balansiranje aktivno i da se podaci raspoređuju među šardovima.
Korak 10
Poslednji korak je provera statusa deljenja. Status se može proveriti pokretanjem naredbe ispod na mongos ruter čvoru.
Status deljenja
Proverite status deljenja pokretanjem naredbe ispod na mongos ruter čvoru.
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>
Distribucija podataka
Mongos ruter raspoređuje opterećenje među šardovima na osnovu ključa šarda i ravnomerno distribuira podatke; kada to postane neophodno, balansiranje se pokreće.
Ključne komponente za distribuciju podataka među šardovima su:
- Balanser igra ulogu u balansiranju podskupova podataka između čvorova sa deljenjem. Balanser se aktivira kada Mongos server počne da distribuira opterećenje između šardova. Kada je aktiviran, Balanser ravnomernije distribuira podatke. Za proveru statusa balansera, pokrenite sh.status() ili
sh.getBalancerState()
ilish.isBalancerRunning()
.
mongos> sh.isBalancerRunning()
true
mongos>
ILI
mongos> sh.getBalancerState()
true
mongos>
Nakon unosa podataka, možemo primetiti aktivnost u Mongos demonu, koja pokazuje da se određeni delovi premeštaju između šardova. Balanser će biti aktivan, pokušavajući da izbalansira podatke između šardova. Pokretanje balansera može dovesti do problema sa performansama; stoga se predlaže pokretanje balansera unutar određenog vremenskog okvira za balansiranje.
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>
- Ključ šarda određuje logiku za distribuciju dokumenata kolekcije sa deljenjem među šardovima. Ključ šarda može biti indeksirano polje ili složeno indeksirano polje, koje mora biti prisutno u svim dokumentima kolekcije koja se ubacuje. Podaci se dele u delove, a svaki deo je povezan sa ključem šarda na osnovu opsega. Na osnovu upita opsega, ruter će odlučiti koji će šard pohraniti podatak.
Ključ šarda se može odabrati uzimajući u obzir pet karakteristika:
- Kardinalnost
- Distribucija pisanja
- Distribucija čitanja
- Ciljanje čitanja
- Lokalitet čitanja
Idealan ključ šarda omogućava MongoDB-u da ravnomerno rasporedi opterećenje na sve šardove. Izbor dobrog ključa šarda je od izuzetnog značaja.
Uklanjanje čvora šarda
Pre uklanjanja šarda iz klastera, korisnik mora da osigura bezbednu migraciju podataka na preostale šardove. MongoDB obezbeđuje sigurno prebacivanje podataka na druge čvorove pre uklanjanja potrebnog čvora šarda.
Pokrenite naredbu ispod da biste uklonili željeni šard.
Korak 1
Prvo, potrebno je da odredimo ime hosta za šard koji treba ukloniti. Naredba ispod će prikazati sve šardove prisutne u klasteru zajedno sa statusom šarda.
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)
}
}
}
Korak 2
Izvršite naredbu ispod da biste uklonili željeni šard iz klastera. Nakon izvršavanja naredbe, balanser se brine o uklanjanju delova iz čvora za ispražnjavanje, a zatim balansira distribuciju preostalih delova između ostalih čvorova.
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)
}
}
}
Korak 3
Da biste proverili status pražnjenja, ponovo izvršite istu naredbu.
db.adminCommand( { removeShard: “rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022” })
Potrebno je da sačekamo da se pražnjenje podataka završi. Polja msg i state će pokazati da li je pražnjenje podataka završeno ili ne, kao što je prikazano ispod:
"msg" : "draining ongoing",
"state" : "ongoing",
Status možemo proveriti i pomoću komande sh.status(). Kada se ukloni, čvor sa deljenjem se neće odraziti u izlazu. Međutim, ako je pražnjenje u toku, čvor sa deljenjem će imati status pražnjenja postavljen na true.
Korak 4
Nastavite da proveravate status pražnjenja istom naredbom dok se željeni šard potpuno ne ukloni.
Kada se završi, izlaz naredbe će prikazati poruku i stanje kao completed.
"msg" : "removeshard completed successfully",
"state" : "completed",
"shard" : "rs1",
"ok" : 1,
Korak 5
Na kraju, potrebno je da proverimo preostale šardove u klasteru. Za proveru statusa unesite sh.status() ili 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)
}
}
}
Ovde možemo videti da uklonjeni šard više nije prisutan na listi šardova.
Prednosti deljenja u odnosu na replikaciju
- U replikaciji, primarni čvor upravlja svim operacijama pisanja, dok su sekundarni serveri potrebni da održavaju rezervne kopije ili služe za operacije samo za čitanje. Međutim, kod deljenja, zajedno sa replika setovima, opterećenje se distribuira među više servera.
- Jedan replika set je ograničen na 12 čvorova, ali ne postoji ograničenje u broju šardova.
- Replikacija zahteva vrhunski hardver ili vertikalno skaliranje za upravljanje velikim skupovima podataka, što je skupo u poređenju sa dodavanjem dodatnih servera u deljenju.
- U replikaciji, performanse čitanja se mogu poboljšati dodavanjem više podređenih/sekundarnih servera, dok će kod deljenja performanse čitanja i pisanja biti poboljšane dodavanjem više čvorova šarda.
Ograničenja deljenja
- Klaster sa deljenjem ne podržava jedinstveno indeksiranje preko šardova, osim ako jedinstveni indeks nema prefiks sa punim ključem šarda.
- Sve operacije ažuriranja za kolekciju sa deljenjem na jednom ili više dokumenata moraju da sadrže ključ šarda ili polje _id u upitu.
- Kolekcije se mogu deliti ako njihova veličina ne prelazi određeni prag. Ovaj prag se može proceniti na osnovu prosečne veličine svih ključeva i konfigurirane veličine delova.
- Deljenje ima operativna ograničenja u pogledu maksimalne veličine kolekcije ili broja delova.
- Izbor pogrešnih ključeva šarda dovodi do problema sa performansama.
Zaključak
MongoDB nudi ugrađeno deljenje za implementaciju velikih baza podataka bez ugrožavanja performansi. Nadamo se da će vam navedene informacije pomoći da podesite MongoDB deljenje. Zatim, možete da se upoznate sa nekim od najčešće korišćenih MongoDB komandi.