„MongoDB” NoSQL duomenų bazių panaudojimas

Kodėl apskritai kalbame apie NoSQL?

Prieš kokius dešimt metų daugelis iš mūsų net negalvojo, kad tradicinės SQL duomenų bazės gali tapti problema. Tačiau pasaulis pasikeitė – dabar turime milijonus vartotojų, kurie generuoja terabaitus duomenų per sekundę, o verslo reikalavimai keičiasi greičiau nei spėjame atnaujinti schemą. Štai čia ir atsiranda MongoDB – viena populiariausių NoSQL duomenų bazių, kuri žada lankstumą, greitį ir horizontalų skalabilumą.

MongoDB nėra vien dar vienas hype’as. Ji išties sprendžia realias problemas, su kuriomis susiduria modernios aplikacijos. Dokumentų orientuota struktūra leidžia saugoti duomenis taip, kaip juos mąstome programavimo kalbose – kaip objektus ar JSON struktūras. Nereikia begalės JOIN operacijų, nereikia ORM magiškų triukų, kurie kartais veikia, o kartais ne.

Žinoma, MongoDB nėra sidabrinė kulka. Yra projektų, kuriuose ji puikiai tinka, ir yra tokių, kur geriau pasirinkti PostgreSQL ar kitą reliacinę duomenų bazę. Bet apie tai pakalbėsime vėliau.

Kur MongoDB tikrai spindi

Dokumentų bazės architektūra daro MongoDB idealią tam tikroms užduotims. Pirmiausia – content management sistemoms. Kai turite straipsnius su skirtingais laukais, komentarus, metaduomenis, kategorijas – visa tai puikiai telpa į vieną dokumentą. Nereikia kurti dešimties lentelių su ryšiais, kurie vėliau komplikuoja kiekvieną užklausą.

Real-time analytics – dar viena sritis, kur MongoDB jaučiasi kaip namie. Agregacijos framework’as leidžia atlikti sudėtingas analitines operacijas tiesiogiai duomenų bazėje. Taip, kartais reikia pagalvoti, kaip optimizuoti pipeline, bet rezultatai būna įspūdingi. Esu matęs projektus, kur MongoDB apdoroja milijonus įvykių per minutę, generuodama realaus laiko ataskaitas.

Mobiliosios aplikacijos taip pat mėgsta MongoDB. Realm (MongoDB įsigyta technologija) leidžia sinchronizuoti duomenis tarp įrenginių ir serverio beveik be papildomo kodo. Offline režimas, konfliktų sprendimas – visa tai veikia iš dėžės. Kai kūriau vieną projektą su React Native, šis funkcionalumas sutaupė mėnesį darbo.

E-commerce platformos – klasikinis pavyzdys. Produktai gali turėti visiškai skirtingas savybes: marškinėliams reikia dydžio ir spalvos, elektronikai – techninių specifikacijų, knygoms – ISBN ir autorių. Reliacinėje bazėje baigtųsi EAV (Entity-Attribute-Value) košmaru arba dešimtimis tuščių stulpelių. MongoDB? Tiesiog kitas dokumentas su kitais laukais.

Praktiniai dalykai, kuriuos reikia žinoti

Pradedant dirbti su MongoDB, pirmasis dalykas – indeksai. Taip, žinau, skamba nuobodžiai, bet be jų jūsų užklausos bus lėtos kaip vėžlys. Ir ne bet kokie indeksai – compound indeksai, kurie atitinka jūsų užklausų šablonus. Naudokite explain() metodą religingai. Jei matote COLLSCAN – jūs turite problemą.

„`javascript
db.users.createIndex({ email: 1, status: 1 })
db.users.find({ email: „[email protected]”, status: „active” }).explain(„executionStats”)
„`

Schema design MongoDB pasaulyje – tai menas. Pagrindinis klausimas: embedded ar referenced? Jei duomenys visada skaitomi kartu – embed’inkite. Jei duomenys dažnai atnaujinami atskirai arba gali augti neribotai – naudokite nuorodas. Pavyzdžiui, komentarai po straipsniu: jei jų būna 5-10, galite įdėti į straipsnio dokumentą. Jei gali būti tūkstančiai – geriau atskirti.

Dar vienas dalykas, kurį dažnai pamirštame – document size limit. MongoDB dokumentas negali viršyti 16MB. Skamba daug, bet kai pradedi embed’inti masyvus su subdokumentais, gali greitai prisiartinti prie ribos. Esu matęs production sistemą, kuri krito būtent dėl šios priežasties – kažkas sprendė saugoti visą vartotojo veiklos istoriją viename dokumente.

Transakcijos ir duomenų vientisumas

Ilgą laiką MongoDB kritikai mėgdavo sakyti: „O kaip su ACID transakcijomis?” Nuo 4.0 versijos MongoDB palaiko multi-document transakcijas, nuo 4.2 – distributed transakcijas per shards. Bet štai kas įdomu – dažniausiai jų nereikia.

Teisingai suprojektavus schemą, daugelis operacijų tampa atominėmis iš prigimties. Atnaujinate vieną dokumentą? Tai atominė operacija. Nereikia BEGIN/COMMIT ceremonijų. Štai kodėl schema design toks svarbus – jis lemia ne tik našumą, bet ir tai, ar apskritai reikės transakcijų.

Tačiau kai transakcijos reikalingos – jos veikia. Pavyzdžiui, bankinė sistema, kur reikia perkelti pinigus tarp sąskaitų:

„`javascript
const session = client.startSession();
session.startTransaction();
try {
await accounts.updateOne(
{ _id: fromAccount },
{ $inc: { balance: -amount } },
{ session }
);
await accounts.updateOne(
{ _id: toAccount },
{ $inc: { balance: amount } },
{ session }
);
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
„`

Bet atkreipkite dėmesį – transakcijos turi našumo kainą. Jos reikalauja papildomos koordinacijos, ypač distribuotoje sistemoje. Todėl naudokite jas tik ten, kur tikrai būtina.

Replikacija ir high availability

Vienas dalykas, kuris MongoDB daro tikrai gerai – replica sets. Tai ne tik backup’as, tai automatinis failover mechanizmas. Turite tris serverius (rekomenduojamas minimumas), vienas – primary, kiti – secondary. Primary krenta? Per kelias sekundes vienas iš secondary tampa nauju primary. Aplikacija net nepastebi.

Konfigūruoti replica set’ą nėra raketų mokslas, bet yra niuansų. Pirma, visada turėkite nelyginį narių skaičių arba naudokite arbiter. Tai būtina, kad split-brain situacijoje galėtų įvykti rinkimai. Antra, pagalvokite apie read preferences. Ar galite skaityti iš secondary? Tai sumažina primary apkrovą, bet duomenys gali būti šiek tiek pasenę.

„`javascript
const client = new MongoClient(uri, {
replicaSet: ‘myReplicaSet’,
readPreference: ‘secondaryPreferred’,
w: ‘majority’,
wtimeout: 5000
});
„`

Write concern – dar viena svarbi koncepcija. w: 1 reiškia, kad operacija laikoma sėkminga, kai primary patvirtina. w: 'majority' – kai dauguma replica set narių patvirtina. Antrasis variantas lėtesnis, bet saugesnis. Jei primary nukris prieš replikuojant duomenis, su w: 1 galite juos prarasti.

Sharding: kai vienas serveris nebepakanka

Horizontalus skalabilumas – viena pagrindinių MongoDB žadamų žemių. Sharding leidžia paskirstyti duomenis per kelis serverius, kiekvienam tvarkant tik dalį duomenų. Skamba puikiai, bet praktikoje tai sudėtinga.

Pirmiausia reikia pasirinkti shard key. Tai sprendimas, kurį vėliau pakeisti beveik neįmanoma (bent jau nebuvo lengva iki naujausių versijų). Geras shard key turi:
– Didelį cardinalumą (daug unikalių reikšmių)
– Tolygų pasiskirstymą (ne visi duomenys krenta į vieną shard’ą)
– Atitikti dažniausias užklausas (kad nebūtų scatter-gather)

Blogas pavyzdys – createdAt data. Visi nauji įrašai krenta į tą patį shard’ą, kiti lieka tušti. Geresnis – userId arba hash’as. Dar geriau – compound key, pvz., { country: 1, userId: 1 }, jei dažnai filtruojate pagal šalį.

Sharding prideda sudėtingumo ne tik duomenų bazės lygmenyje, bet ir aplikacijos. Transakcijos tampa lėtesnės. Kai kurios operacijos, kaip $lookup per skirtingus shard’us, gali būti nepalaikomos arba labai neefektyvios. Todėl mano patarimas – pradėkite su replica set, pereikite prie sharding tik kai tikrai reikia.

Monitoringas ir optimizavimas

Production sistemoje be monitoringo – kaip skraidymas aklai. MongoDB teikia MongoDB Atlas – managed servisą su įmontuotu monitoringu. Jei naudojate self-hosted, reikės MongoDB Ops Manager arba trečiųjų šalių įrankių kaip Datadog, Prometheus su MongoDB exporter.

Ką stebėti? Pirma, slow queries. Įjunkite profiling bent development aplinkoje:

„`javascript
db.setProfilingLevel(1, { slowms: 100 })
„`

Tai įrašys visas užklausas, kurios trunka ilgiau nei 100ms. Production’e galbūt norėsite didesnės reikšmės, bet idėja ta pati – identifikuoti problemas anksčiau nei jos tampa kritinėmis.

Working set – duomenų kiekis, kuris aktyviai naudojamas. Idealiu atveju jis turėtų tilpti RAM’e. Jei ne, pradėsite matę disk I/O, o tai reiškia lėtėjimą. MongoDB naudoja WiredTiger storage engine, kuris puikiai tvarko cache’avimą, bet negali stebuklauti, jei duomenų per daug.

Connection pool’ai – dar viena dažna problema. MongoDB turi connection limitą (defaultinis 65536, bet praktiškai mažesnis). Jei turite mikroservisų armiją, kiekvienas su savo connection pool, galite greitai išsemti limitus. Naudokite connection pooling protingai:

„`javascript
const client = new MongoClient(uri, {
maxPoolSize: 10,
minPoolSize: 5,
maxIdleTimeMS: 30000
});
„`

Kai MongoDB nėra tinkamas pasirinkimas

Būkime sąžiningi – MongoDB ne visur tinka. Jei jūsų duomenys labai struktūrizuoti, su sudėtingais ryšiais tarp lentelių, ir jums reikia sudėtingų JOIN operacijų – PostgreSQL ar MySQL bus geresnis pasirinkimas. MongoDB $lookup operatorius egzistuoja, bet jis nėra toks efektyvus kaip SQL JOIN.

Finansinės sistemos, kur ACID transakcijos yra kritinės, taip pat geriau jaučiasi su tradicinėmis duomenų bazėmis. Taip, MongoDB dabar turi transakcijas, bet jos nėra tokios brandžios ir optimizuotos kaip PostgreSQL ar Oracle.

Jei jūsų komanda gerai moka SQL ir neturi laiko mokytis naujų paradigmų – nekeiskite to, kas veikia. MongoDB turi mokymosi kreivę, ypač schema design aspekte. Blogai suprojektuota MongoDB schema gali būti blogesnė nei bet kokia SQL schema.

Dar vienas aspektas – ad-hoc užklausos. Jei turite daug analitikų, kurie rašo sudėtingas užklausas tiesiogiai į duomenų bazę, SQL bus draugiškesnis. MongoDB užklausų sintaksė nėra tokia intuityvi ne-programuotojams.

Realybė po hype’o

MongoDB praėjo kelią nuo „web scale” meme iki brandaus enterprise produkto. Taip, buvo laikotarpis, kai ji buvo pernelyg hype’inama ir naudojama ten, kur neturėjo būti. Bet dabar, su metų patirtimi ir brandesnėmis versijomis, ji yra tikrai geras įrankis tinkamose situacijose.

Jei kuriate aplikaciją su lankstoma schema, kur duomenų struktūra gali keistis, jei reikia greito prototipavimo, jei planuojate horizontalų skalabilumą – MongoDB verta rimto apsvarstymo. Bet pradėkite paprastai. Nereikia iš karto sharding’o ir distributed transakcijų. Pradėkite su vienu serveriu, pereikite prie replica set, o sharding’ą palikite paskutiniam momentui.

Investuokite laiko į schema design. Tai nėra „schemaless”, tai „flexible schema”. Blogai suprojektuota schema sukels daugiau problemų nei bet kuri kita technologinė klaida. Skaitykite dokumentaciją, mokykitės iš kitų klaidų, testuokite su realistiškais duomenų kiekiais.

Ir nepamirškite – duomenų bazė yra tik įrankis. Svarbiausia yra išspręsti verslo problemą, o ne naudoti naujausią technologiją dėl CV. Kartais geriausia duomenų bazė yra ta, kurią jūsų komanda jau moka naudoti.

Parašykite komentarą

El. pašto adresas nebus skelbiamas. Būtini laukeliai pažymėti *