Přejít k navigační liště

Zdroják » Databáze » Praktické použitie MongoDB v .NET

Praktické použitie MongoDB v .NET

Články Databáze

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.

Komentáře

Subscribe
Upozornit na
guest
11 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
Tomáš Herceg

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ý).

igorkulman

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ý.

Logik

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ší.

igorkulman

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.

Roman Antoň

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í :-)

fos4

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.

ghjfghj

XML… a nebo JSON?

Ladislav Thon

> 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

Majkl

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?

ghjfghj

Mongo jsem si oblibil, bohuzel pri prechodu na linugz a Mono jedina komponenta, ktera nefungovala bylo prave Mongo

xor

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.

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.

Pocta C64

Za prvopočátek své programátorské kariéry vděčím počítači Commodore 64. Tehdy jsem genialitu návrhu nemohl docenit. Dnes dokážu lehce nahlédnout pod pokličku. Chtěl bych se o to s vámi podělit a vzdát mu hold.