Psikutas bez “s” czyli Big Data po mongo(l)sku

https://xpil.eu/TOBFQ

Niech si臋 Czytelnik nie da zwie艣膰 g艂臋bi膮 tytu艂u dzisiejszego wpisu, nie o andrologii kynologicznej bowiem dzi艣 b臋dzie, tylko o technologiach przetwarzania danych. A konkretnie - du偶ych zbior贸w danych, o niekoniecznie z g贸ry ustalonej (b膮d藕 znanej) strukturze, czyli o tym, co od jakiego艣 czasu otrzyma艂o znamienn膮 nazw臋 "Big Data".

Jak si臋 niekt贸rzy z moich Czytelnik贸w by膰 mo偶e orientuj膮, ostatnimi czasy tematyka "Big Data" sta艂a si臋 bardzo popularna. W prasie bran偶owej ju偶 od kilku lat mo偶na znale藕膰 mn贸stwo odniesie艅 do r贸偶nych (nie zawsze udanych) projekt贸w opartych o Big Data. Kilku producent贸w oprogramowania do przetwarzania danych deklaruje (mniej lub bardziej prawdziwie), 偶e ich produkty obs艂uguj膮 Big Data - i tak dalej.

W zwi膮zku z tym sam niedawno zainteresowa艂em si臋 tematem. Na pierwszy (i p贸ki co - jedyny) ogie艅 posz艂a technologia MongoDB (Mongo czyli Mongo艂 bez "艂" - st膮d w艂a艣nie pomys艂 na 贸w szalenie zabawny, khem, nieprawda偶, tytu艂 wpisu).

Czemu akurat Mongo, a nie na przyk艂ad Hadoop albo Dynamo? Nie mam poj臋cia, pewnie przypadkiem. Tak czy siak, zainteresowa艂em si臋 MongoDB, zainstalowa艂em sobie toto ustrojstwo na domowym kompie, pobawi艂em si臋 par臋 dni, potestowa艂em r贸偶ne r贸偶no艣ci - dzi艣 kr贸tko opisz臋 swoje wra偶enia.

Ma si臋 rozumie膰, prosz臋 traktowa膰 ten wpis cum grano salis - 偶aden ze mnie fachowiec. Ot, przechodzie艅, kt贸ry napatoczy艂 si臋 w okolice Luwru i teraz opowiada znajomym, 偶e widzia艂 par臋 fiku艣nych landszaft贸w.

Mongo jest baz膮 danych typu NoSQL, a wi臋c zapytania do bazy odbywaj膮 si臋 bez magicznego SELECT ... FROM...

Zamiast tego ca艂o艣膰 komunikacji z serwerem Mongo odbywa si臋 za pomoc膮 sk艂adni JSON/BSON (BSON to binarna wersja JSON). R贸wnie偶 dane na serwerze s膮 sk艂adowane w formacie BSON, dzi臋ki czemu warstwa t艂umacz膮ca zapytania na wewn臋trzny j臋zyk struktur danych ma mniej pracy ni偶 w przypadku tradycyjnych silnik贸w baz danych. Tak my艣l臋.

Dane w Mongo pouk艂adane s膮 w nast臋puj膮cej hierarchii: bazy danych => kolekcje => dokumenty => atrybuty. Kolekcja odpowiada tabeli (tradycyjnie rozumianej), tylko bez 艣cis艂ej struktury kolumn, natomiast dokument odpowiada rekordowi w tabeli - z tym, 偶e ka偶dy taki "rekord" w kolekcji mo偶e (ale nie musi) mie膰 inny zestaw "kolumn" (czyli atrybut贸w). Troch臋 to na pocz膮tku przeszkadza, zw艂aszcza jak si臋 cz艂owiek przesiada z tradycyjnej, strukturalnej bazy danych - ale przy odpowiednim podej艣ciu do tematu okazuje si臋 to od艣wie偶aj膮co wygodne. W tradycyjnym podej艣ciu, je偶eli mamy tabel臋 z milionem rekord贸w historycznych, a potem dodamy do niej kolumn臋, musimy albo zaakceptowa膰 w tej kolumnie warto艣ci puste (null) albo wype艂ni膰 t臋 kolumn臋 danymi. A w Mongo, po prostu zaczynamy dopisywa膰 do kolekcji kolejne dokumenty z nowym atrybutem - i nie ma konfliktu, "stare" dokumenty tego atrybutu nie maj膮, a "nowe" - tak. Proste? Proste...

Najwi臋ksz膮 bol膮czk膮 tradycyjnych system贸w bazodanowych jest - na og贸艂 - wysokowydajna implementacja operatora JOIN. Je偶eli mamy dwie tabele: klienci oraz faktury, faktury zazwyczaj maj膮 kolumn臋 z identyfikatorem klienta. 呕eby wyci膮gn膮膰 dane z obydwu tabel na raz, trzeba wykona膰 operacje JOIN mi臋dzy tymi dwiema tabelami. O ile w tym przypadku taki JOIN b臋dzie w miar臋 prosty (przy za艂o偶eniu poprawnej strategii indeksowania), o tyle w niekt贸rych bardziej zaawansowanych przypadkach (na przyk艂ad, znajd藕 mi wszystkich klient贸w, kt贸rzy maj膮 wystawione faktury, w kt贸rych co najmniej jeden towar ma dat臋 produkcji odleg艂膮 od daty wystawienia faktury o wi臋cej ni偶 siedem miesi臋cy, ale kt贸ry jednocze艣nie nie zawiera w nazwie 艂a艅cucha "exp", chyba 偶e faktura jest wystawiona w stanie Ohio) taki JOIN mo偶e ju偶 by膰 bardzo kosztowny i przy niew艂a艣ciwym podej艣ciu mo偶e zapcha膰 serwer.

W MongoDB operatora JOIN po prostu nie ma. Powy偶szy przypadek (klienci - faktury) b臋dzie w Mongo reprezentowany albo w postaci pojedynczej kolekcji Klienci, z zagnie偶d偶on膮 w ka偶dym dokumencie tablic膮 faktur, albo te偶 b臋d膮 to dwie osobne kolekcje, jednak w贸wczas wyszukiwanie faktury danego klienta (b膮d藕 klient贸w) b臋dzie odbywa艂o si臋 dwutorowo, po stronie aplikacji: najpierw odczytujemy dokument z kolekcji Klienci, w nim odszukujemy identyfikatory po艂膮czonych faktur, i w osobnym zapytaniu odczytujemy te faktury z kolekcji Faktury. B臋dzie si臋 to odbywa艂o asynchronicznie (co ma swoje wady i zalety), ale nie obci膮偶y serwera operacj膮 JOIN, niezale偶nie od ilo艣ci danych oraz stopnia komplikacji zale偶no艣ci mi臋dzy nimi. Z drugiej strony, znacz膮co zwi臋kszy ilo艣膰 zapyta艅 do serwera, je偶eli b臋dziemy chcieli w p臋tli przelecie膰 wszystkich klient贸w (dwuznaczno艣膰 niezamierzona) i powyci膮ga膰 ich faktury.

Sam format JSON jest nieco inny od wszystkiego, z czym si臋 dotychczas spotka艂em. Mo偶e si臋 nieco kojarzy膰 z XML-em (i, jak si臋 okazuje, jest w pe艂ni przet艂umaczalny na XML, w obie strony), jednak jest bardziej ode艅 zwarty i (moim zdaniem) czytelny.

Najmniejsz膮, niepodzieln膮 cz臋艣ci膮 dokumentu JSON jest para atrybut:warto艣膰. W przypadku XML by艂oby to prawdopodobnie <atrybut>warto艣膰</atrybut> - jak wida膰, wersja z JSON jest kr贸tsza. Za pomoc膮 sk艂adni JSON przekazujemy zar贸wno dane jak i parametry zapyta艅 do bazy.

Przyk艂ad dokumentu:

{
    _id : 1,
    nazwisko : 'Kowalski',
    imie : 'Jan',
    data_urodzenia : '12 stycznia 1963',
    PESEL : '63011257601'
}

Powy偶szy dokument m贸g艂by by膰 elementem w kolekcji Osoby. Oto w jaki spos贸b mo偶na by go tam doda膰:

db.Osoby.insert({_id : 1,nazwisko : 'Kowalski',imie : 'Jan',data_urodzenia : '12 stycznia 1963',PESEL :'63011257601'}

Mo偶emy te偶 wstawi膰 do kolekcji kilka dokument贸w na raz:

db.Osoby.insert(
    [
        {_id : 1,nazwisko : 'Kowalski',imie : 'Jan',data_urodzenia : '12 stycznia 1963',PESEL :'63011257601'},
        {_id : 2,nazwisko : 'Malinowski',imie : 'Albert',data_urodzenia : '21 Wrze艣nia 2008',PESEL :'21090855024'},
        {_id : 3,nazwisko : 'Czechowicz',imie : 'Klementyna',data_urodzenia : '1 czerwca 1926',PESEL :'01062633208'}
    ]
)

Je偶eli nie podamy pola _id, Mongo utworzy to pole za nas i wype艂ni je unikalnymi warto艣ciami typu OID (96-bitowa liczba, w reprezentacji szesnastkowej 24 znaki hex). Pole _id jest obowi膮zkowe dla ka偶dego dokumentu i musi by膰 unikalne w obr臋bie kolekcji. Typ danych tego pola mo偶e by膰 dowolny, co wi臋cej, r贸偶ne dokumenty w obr臋bie tej samej kolekcji mog膮 mie膰 pole _id r贸偶nych typ贸w. Wa偶na jest wy艂膮cznie unikalno艣膰.

Je偶eli w powy偶szym przyk艂adzie kolekcja Osoby nie istnieje, zostanie automatycznie utworzona (w przeciwie艅stwie do podej艣cia tradycyjnego, gdzie trzeba najpierw utworzy膰 tabel臋 za pomoc膮 CREATE TABLE ... a dopiero potem wype艂nia膰 j膮 danymi).

I jeszcze przyk艂ad pokazuj膮cy niejednorodno艣膰 danych w obr臋bie kolekcji:

Najpierw skasujmy kolekcj臋 Osoby:

db.Osoby.remove()

A nast臋pnie:

db.Osoby.insert(
    [
        {_id : 1,nazwisko : 'Kowalski',imie : 'Jan',data_urodzenia : '12 stycznia 1963',PESEL :'63011257601'},
        {_id : 2,nazwisko : 'Malinowski',data_urodzenia : '21 Wrze艣nia 2008',PESEL :'21090855024'},
        {_id : 3,nazwisko : 'Czechowicz',imie : 'Klementyna',PESEL :'01062633208',kolor_wlosow:'czarny'}
    ]
)

Jak wida膰, tym razem 偶adne dwa dokumenty w kolekcji Osoby nie maj膮 takiej samej struktury. Nie stanowi to jednak 偶adnej przeszkody - pr贸ba odczytania imienia pana Malinowskiego zwr贸ci NULL, podobnie jak pr贸ba odczytania koloru w艂os贸w Kowalskiego. Jest to (przynajmniej w teorii) nieco ba艂aganiarskie, jednak na d艂u偶sz膮 met臋 bardzo wygodne. Trzeba po prostu zmieni膰 spos贸b my艣lenia.

No w艂a艣nie - a w jaki spos贸b zapyta膰 o imi臋 Kowalskiego?

O tak:

db.Osoby.find( { nazwisko : 'Kowalski'}, {imie : 1})

Operator find() przyjmuje dwa parametry (obydwa opcjonalne). Pierwszy to wyra偶enie filtruj膮ce (a wi臋c, czego szukamy), a drugie m贸wi, kt贸re atrybuty chcemy wy艣wietli膰 na wyj艣ciu. Ponadto, atrybut _id jest domy艣lnie zawsze w艂膮czony, a wi臋c powy偶sze zapytanie zwr贸ci nam imiona wszystkich os贸b o nazwisku Kowalski, wraz z ich unikalnymi identyfikatorami. Je偶eli chcemy wy艂膮czy膰 zwracanie kolumny _id, musimy to explicite wyszczeg贸lni膰, o tak:

db.Osoby.find( { nazwisko : 'Kowalski'}, {imie : 1, _id : 0})

Rzecz jasna czasem chcieliby艣my przeszuka膰 kolekcj臋 pod k膮tem wi臋cej ni偶 jednego parametru, na przyk艂ad szukamy wszystkich Kowalskich urodzonych 12 stycznia 1963 roku:

db.Osoby.find( { nazwisko : 'Kowalski', data_urodzenia : '12 stycznia 1963'}, {})

Czasem zale偶y nam na wyszukaniu wed艂ug zakresu. Na przyk艂ad, znajd藕 wszystkie osoby, kt贸re maj膮 co najmniej siedem palc贸w:

db.Osoby.find({ile_palcow : {$gte : 7}}, {})

W przyk艂adzie powy偶ej wyra偶enie filtruj膮ce {$gte : 7} jest samo w sobie dokumentem - w taki spos贸b mo偶emy konstruowa膰 ca艂kiem skomplikowane filtry do wyszukiwania danych. Jednak maj膮 one pewne ograniczenia. Na przyk艂ad nie da si臋 wyszuka膰 dokument贸w, kt贸re maj膮 jak膮艣 tablic臋 (nie tabel臋 tylko w艂a艣nie tablic臋) o ilo艣ci element贸w z zadanego przedzia艂u. Mo偶e znale藕膰 takie, kt贸re maj膮 w tej tablicy jedn膮, konkretn膮 ilo艣膰 element贸w, ale wg przedzia艂u ju偶 si臋 nie da. Mongo jest jednak aktywnie rozwijane i bardzo mo偶liwe, 偶e brakuj膮ce obecnie opcje znajd膮 si臋 niebawem w kolejnych wersjach.

A w艂a艣nie, tablice. Jeden przyk艂ad ju偶 poda艂em powy偶ej, wstawiaj膮c trzy osoby za pomoc膮 pojedynczej komendy insert(). Tablic臋 definiujemy za pomoc膮 nawias贸w kwadratowych. Mo偶emy mie膰 na ten przyk艂ad tablic臋 wszystkich kraj贸w, kt贸re dana osoba odwiedzi艂a:

{
_id : 1,
nazwisko : 'Kowalski',
imie : 'Jan',
data_urodzenia : '12 stycznia 1963',
PESEL : '63011257601',
kraje : ['PL','HU','GB','IE']
}

Powy偶szy dokument definiuje osob臋, kt贸ra odwiedzi艂a Polsk臋, W臋gry, Wielk膮 Brytani臋 oraz Irlandi臋.

W jaki spos贸b wyszuka膰 teraz wszystkie osoby, kt贸re odwiedzi艂y, dajmy na to, Meksyk?

O tak:

db.Osoby.find({kraje : 'MX'}, {})

A w drug膮 ma艅k臋? Jak znale藕膰 wszystkie osoby, kt贸re nigdy nie by艂y w Polsce?

db.Osoby.find({kraje : {$nin : 'PL'}}, {})

Takich operator贸w jak $nin czy $gte jest ca艂kiem sporo, po szczeg贸艂y przekierowuj臋 do dokumentacji Mongo.

Najwi臋cej problem贸w stwarza - zaskakuj膮co - nie odpytywanie kolekcji o pasuj膮ce dane, tylko stworzenie optymalnego modelu tych danych. Na przyk艂ad klasyczna relacja wiele-do-wielu mi臋dzy ksi膮偶kami a autorami. Co lepiej: kolekcja ksi膮偶ek z list膮 autor贸w w ka偶dym dokumencie reprezentuj膮cym ksi膮偶k臋, a mo偶e na odwr贸t, lista autor贸w z wylistowanymi ksi膮偶kami? Podej艣膰 jest kilka, 偶adne nie jest idealne, ka偶de ma swoje wady i zalety. Ja bym prawdopodobnie utworzy艂 osobn膮, "w膮sk膮" kolekcj臋, w kt贸rej trzyma艂bym pary autor_id-ksi膮偶ka_id, jednak zrobi艂bym tak wy艂膮cznie dlatego, 偶e tak mi podpowiada moje SQL-owe do艣wiadczenie.

Mongo jest od samego pocz膮tku tworzone z my艣l膮 o nadmiarowo艣ci danych oraz skalowalno艣ci w poziomie, w zwi膮zku z czym ma wbudowane mechanizmy do 艂atwego klastrowania oraz definiowania struktur fail-over. Niestety, nie by艂em a偶 tak zajad艂y, 偶eby stawia膰 Mongo na wi臋cej ni偶 jednej maszynie, a wi臋c tematu za bardzo nie zg艂臋bi艂em.

Nie opowiedzia艂em dzi艣 r贸wnie偶 zbyt wiele o bardziej zaawansowanych aspektach pracy z Mongo - jednak, jak ju偶 nadmieni艂em na samym pocz膮tku, jestem tylko przechodniem, kt贸ry zerkn膮艂 na to i owo. Fachowcem zostan臋 (by膰 mo偶e!), kiedy zaczn臋 pracowa膰 na prawdziwych danych. P贸ki co tylko obw膮chuj臋 zagadnienie 馃檪

Aha, najwa偶niejsze na koniec: Mongo jest darmowe. I u偶ywane u takich gigant贸w jak CERN, Cisco, Foursquare czy MTV. Fajnie by艂oby kiedy艣 przesi膮艣膰 si臋 na takie wielkie dane, jakie maj膮 na przyk艂ad w CERN (tam podobno jest osobna farma serwer贸w, odpowiedzialna wy艂膮cznie za wydajne tworzenie nowych partycji dla strumienia danych generowanych z eksperyment贸w - no ale co tu si臋 dziwi膰, 25 petabajt贸w danych rocznie to ca艂kiem niez艂a 艣rednia...)

Wszystkich czytelnik贸w, kt贸rzy tutaj dotarli i jeszcze nie zasn臋li, serdecznie pozdrawiam. Pozosta艂ym m贸wi臋 dobranoc :]

https://xpil.eu/TOBFQ

3 komentarze

  1. E tam Mongo艂y jakie艣 馃槈

    Dbf! to by艂y bazy danych,a do tego dbase, foxpro, clipper.

    Potem si臋 z konieczno艣ci 偶yciowej przerzuci艂em na b-drzewa (nie chodzi o brzozy) czeli b-tree. Ale kto to dzisiaj pami臋ta 馃榾 i o czym ja w og贸le m贸wi臋?!

    1. Ja pami臋tam dbase. O dziwo, jeszcze w 2005 roku mia艂em do czynienia z systemem opartym na dbase, na starym DOS-ie 3.30 bodaj偶e, i za nic w 艣wiecie nie da艂o si臋 go usun膮膰 z firmy i zast膮pi膰 nowszym, bo by艂 nies艂ychanie wa偶ny, chocia偶 jego producent ju偶 dawno nie istnia艂.

      Co do b-drzew, to ostatni raz s艂ysza艂em o nich na studiach. Uda艂o mi si臋 nawet napisa膰 w C++ algorytm r贸wnowa偶膮cy takie b-drzewo, a potem jeszcze prosty (plikowy) silnik bazy danych oparty na b-drzewach. Ale to zamierzch艂a historia jest.

Leave a Comment

Komentarze mile widziane.

Je偶eli chcesz do komentarza wstawi膰 kod, u偶yj sk艂adni:
[code]
tutaj wstaw sw贸j kod
[/code]

Je偶eli zrobisz liter贸wk臋 lub zmienisz zdanie, mo偶esz edytowa膰 komentarz po jego zatwierdzeniu.