Praktické použitie MongoDB v .NET

NoSQL databázy naberajú v súčasnosti na populárnosti, dlhšie som sa chystal nejakú z nich vyskúšať. Pri práci na aktuálnom projekte som mal na to konečne možnosť, zvolil som riešenie Microsoft .NET.
Zadanie problému
Aplikácia, na ktorej som pracoval, komunikovala s MS SQL databázou a LDAPom bežiacimi na vzdialených počítačoch. Jej úlohou bolo každý deň natiahnúť dáta z LDAPu, transformovať ich, porovnať ich stav s aktuálnym stavom v MS SQL a premietnuť do MS SQL všetky zmeny za posledný deň. Hlavný dôraz sa kládol na rýchlosť.
Keďže dopyty do MS SQL databázy (ktorá naviac beží na vzdialenom počítači) sú dosť pomalé, jasnou voľbou bolo čiastkové natiahnutie potrebných záznamov do pamäte a práca s nimi v pamäti. Platforma Microsoft .NET však na 32-bitovom operačnom systéme obsahuje obmedzenie maximálnej alokovanej pamäte na cca 2 GB, vzhľadom na veľké množstvo záznamov (rádovo státisíce) nebolo možné tieto záznamy v pamäti udržiavať. Bolo potrebné nájsť iné rýchle riešenie s podporou dopytovania.
Dôvod použitia NoSQL databázy
Ako dobrý nápad sa zdalo použitie nejakej NoSQL databázy. NoSQL databázy bývajú na rozdiel od klasických SQL databáz optimalizované na veľké množstvo read/write operácií. Celý nápad spočíval v tom, že na stroji, kde beží aplikácia, bude bežať lokálne aj NoSQL databáza. Aplikácia natiahne záznamy zo vzdialeného MS SQL servera a uloží ich do lokálnej NoSQL databázy. Dotazovanie bude namiesto do hash mapy v pamäti prebiehať do NoSQL databázy, kde kľúč pôvodnej hash mapy bude zodpovedať kľúču NoSQL databázy. Dané riešenie bude určite rýchlejšie ako dopytovanie do vzdialenej MS SQL databázy a nenarazí na limit pamäte.
MongoDB
Ako NoSQL databáza bola zvolená MongoDB. Výhodou je, že MongoDB nie je potrebné inštalovať, je dostupná pre rôzne platformy, skladá sa z pár binárnych súborov a vyžaduje len adresár na uloženie dát. Môže byť teda jednoducho distribuovaná spolu s vašim programom. Stačí, ak túto NoSQL databázu spustíte pred behom vášho programu a ukončíte spolu s ním. Prípadne môžete spustenie a ukončenie MongoDB ovládať programovo.
MongoDB a C#
Keďže MongoDB je dokumentová databáza, bolo potrebné ukladané objekty previesť na dokumenty (MongoDB Document). Ovládač MongoDB pre .NET síce obsahuje možnosť ukladať priamo objekty ľubovoľného typu, nefunguje to však úplne spoľahlivo (občasný StackOverflow v mscore.lib).
Prevod objektov na MongoDB Document
Prevod ľubovoľnej triedy na dokument a jej nasledujúcu rekonštrukciu z dokumentu je možné jednoducho vykonať pomocou reflexie, nie je však možné ukladať ako položky dokumentu iné objekty. Ak nemôžete priamo zasahovať do definícií použitých tried môžete v C# použiť extension method alebo partial classes:
partial class AccountInTime { public Document ToDocument() { Document document = new Document(); document["_id"] = AccountId; Type accountInTimeType = this.GetType(); PropertyInfo[] fieldInfo = accountInTimeType.GetProperties(); string[] bannedProperties = {"User", "Account"}; foreach (PropertyInfo info in fieldInfo) { if (!bannedProperties.Contains(info.Name)) { document[info.Name] = info.GetValue(this, null); } } return document; } public AccountInTime(Document document) { Type accountInTimeType = this.GetType(); PropertyInfo[] fieldInfo = accountInTimeType.GetProperties(); foreach (PropertyInfo info in fieldInfo) { info.SetValue(this,document[info.Name],null); } } }
Metóda ToDocument nastaví kľúč _id na vlastnosť AccountId, pomocou reflexie prejde všetky vlastnosti objektu (okrem explicitne zakázaných) a uloží ich do dokumentu. Konštruktor rovnako pomocou reflexie prejde všetky vlastnosti objektu a naplní ich hodnotami z dokumentu.
Práca s MongoDB
Práca so samotnou MongoDB može byť implementovaná napríklad pomocou Singleton triedy, ktorá vytvára spojenie s MongoDB:
internal sealed class MongoRepository { private static MongoRepository _mongoRepository; public static MongoRepository GetInstance() { return _mongoRepository ?? (_mongoRepository = new MongoRepository()); } private readonly Mongo _mongo = new Mongo(); public MongoRepository() { _mongo = new Mongo(); _mongo.Connect(); } }
Pri vkladaní záznamov do MongDB je potrebné získať referenciu na databázu, na požadovanú kolekciu a na vloženie dokument slúži metóda Insert:
public void Insert(IEnumerable<accountintime> accounts) { //databáza var db = _mongo.GetDatabase("to2"); //kolekcia var collection = db.GetCollection("accountintimes"); //zmazanie všetkých záznamov collection.Remove(new Document() { }); foreach (AccountInTime account in accounts) { //vloženie záznamu collection.Insert(account.ToDocument()); } }
Premenná db reprezentuje databázu a premenná collection „tabuľku“. Ak daná databáza alebo kolekcia neexistuje, automaticky sa vytvorí. Metóda collection.Remove() maže záznamy vyhovujúce poskytnutému selektoru, new Document() je univerzálny selektor, ktorému vyhovujú všetky záznamy. Napríklad na zmazanie záznamov s _id=123 by bol použitý selektor new Document(){_id=123}.
Prístup k záznamom uloženým v MongoDB je možný napríklad pomocou IEnumerable rozhrania vhodného na jednorazovú iteráciu:
public IEnumerable<document> AccountInTimes { get { //databáza var db = _mongo.GetDatabase("to2"); //kolecia var collection = db.GetCollection("accountintimes"); //výber všetkých záznamov return collection.FindAll().Documents; } }
V prípade potreby vyhľadania záznamu pomocou nejakého kľúča je možné použiť LINQ a nájdený záznam následne previesť naspäť na objekt pôvodného typu:
AccountInTime dbAccountInTime = new AccountInTime(mongoRepository.AccountInTimes.Where(l => l["_id"].ToString() == accountId.ToString()).SingleOrDefault());
Záver
Použité riešenie s MongoDB fungovalo a vyriešilo pôvodný problém. Prístup k záznamom v lokálnej MongoDB databáze bol síce pomalší ako prístup k záznamov pämati, bol však výrazne rýchlejši ako dopytovanie do vzdialenej MS SQL databázy. Ak budete niekedy potrebovať uložiť veľké množstvo prevažne plochých záznamov do štruktúry podobnej hash mape, určite siahnite po nejakej NoSQL databáze, oplatí sa to.
Nedělal jste nějaké měření, o kolik rychlejší to je? A jestli to je rychlejší než data přelít ze vzdáleného SQL Serveru do lokálního SQL Serveru a hledat to na něm (tam by se dala i velká část kódu znovupoužít, jestliže jste jej už pro SQL Server měl napsaný).
Presné merania som nevykonal, subjektívne to bolo tak 5x rýchlejšie, záleží však, čo sa práve dialo v MS SQL (virtuálny vzdialený stroj s viacerými databázami a rôznymi procesmi).
O lokálnom SQL servery som neuvažoval, na danom stroji MS SQL nebolo a ani nebolo možné ho doinštalovať. Mal som hotový kód pre prácu so záznamami v pamäti a uvedený prechod na MongoDB bol celkom jednoduchý.
A zvážil jste použití „embeded“ verzí databází, např. SQLite, embeded firebird etc? Přepisovat byste nemusel v podstatě nic, pouze na začátku přetáhnout záznamy a vytvořit spojení do lokální databáze.
Nicméně, jestli nepotřebujete žádné vyspělé fíčury SQL, mohlo to být vcelku dobré řešení, i když imho by úprava na embeded sql server byla rychlejší.
Pokiaľ viem tak žiadna z embeded databázy nepodporuje úplne LINQ, takže toho kódu by bolo potrebné vytvoriť celkom dosť. Okrem toho som nepotreboval žiadne SQL vlastnosti,len ploché uložene státisícov záznamov.
Osobně bych ve vašem případě sáhl po MS SQL Server Compact. Podle všeho by mohl splňovat Vaše požadavky, které jste zmínil – neinstaluje se a podporuje LING (verze 4). Přenostitelnost kódu z „dospělého“ MS SQL Serveru by byla jednoznačná.
Vaše volba by se však také dala považovat za vytvoření si příležitosti pro seznámení s kategorií noSQL databází :-)
U nas jsme mongoDB nasadili abychom mohli vyhledávat v XML dokumentech (faktury) a dobře se to osvědčilo. Nejen že to funguje jako backup, ale lze v tom i vyhledavat.
XML nacteme jako simple_xml, prevedeme na pole a ulozime. Zadna prace navic neni zapotrebi.
XML… a nebo JSON?
> Platforma Microsoft .NET však na 32-bitovom operačnom systéme obsahuje obmedzenie maximálnej alokovanej pamäte na cca 2 GB
Největší prča je, že MongoDB na 32bitové platformě nezvládne víc než cca 2.5 GB dat, protože datové soubory mapuje do paměti :-) 32-bit limitations
No já bych chtěl hlavně vidět tu konzistenci dat v ostrém provozu při takovém návrhu „řešení“. Už to úvodní přemýšlení o „…jasnou voľbou bolo čiastkové natiahnutie potrebných záznamov do pamäte…“ při statisících záznamů o mnohém svědčí ;-)
I při té NoSQL dB jako vypnete provoz pro uživatele a update v target dB se bude dělat v „single“ režimu? Co když zpracování uprostřed spadne (z různých důvodů), tak všechno se bude dělat znovu? Co pak příp. duplicity, rozpracované záznamy?
Mongo jsem si oblibil, bohuzel pri prechodu na linugz a Mono jedina komponenta, ktera nefungovala bylo prave Mongo
Tos‘ mě vyděsil. Mongo používám přes MONO na linuxu jako mezisklad dat pro vyhledávač a doteď jsem žil v přesvědčení, že mi to funguje.