Przetwarzanie wielkich wolumenów danych

1

Cześć, za pewne to częsty problem w firmach.
Mam bardzo dużą bazę danych, z których użytkownik może sobie wygenerować raport.
Zastanawiam się jak to dobrze zrobić, żeby nie położyć środowiska i nie zapchać całego ramu,
Używacie jakichś dostępnych już narzędzi w środowisku Javowo - Springowym?

Dla Spring Batch przypadek jest chyba odwrotny, czyli mamy ogromnego XML i wrzucamy go do DB. Chyba, że można też w drugą stronę.

Prośba o tipy zanim się Proof of conceptów naklepię :D

3

Poczytaj o hurtowaniach danych i hadoopie. Generalnie na produkcji nie generujemy raportów na transakcyjnych (w sensie do przetwarzania danych online) bazach danych. Java ze Springiem jednak nie do wszystkiego się nada ;)

0

@Charles_Ray: no nie nada się do wszystkiego. I niestety lipa jest.
Jak zrobię szybkie rozwiązanie to padną serwery i będzie na mnie.
Jak nie zrobię rozwiązania to nie będzie raportów i będzie dym i też na mnie :D
Na Hadoopa nie mam czasu, a i tak go nie umiem nic a nic.

Jak wrzucę możliwość generowania raportów to mam produkcję na głowie w Boże Narodzenie i ogólny ambaras ...

@0xmarcin nie są to dane tej skali jak napisałeś, ale duże na tyle, że jak te rekordy załaduję do pamięci i zacznę przetwarzać to wystarczy kilka na raz takich operacji i wszystko położone.

3

Szybki pomysł jest taki: zrób replikację do instancji read-only. Generuj raporty na tej drugiej instancji. Przy odrobinie szczęścia już taką replikę posiadasz, jak nie to warto o tym pomyśleć.

Bez podania nazwy i typu DB nie wiele można Ci pomóc, np. MS'owy sikłel-serwer ma bardzo dobre wsparcie do raportowania/hurtowni danych. Z kolei na Oracle to ja się np. w ogóle nie znam więc bym się nawet nie wypowiadał.

0

@0xmarcin: używamy Oraclowej DB. Replikacja o ile się nie mylę też jest wdrożona.
Coś myślę, że faktycznie nowy serwis (czy mikro to nie wiem) do generowania tych raportów by się przydał. Z tym, że też np, użytkownik kliknie: dej mi raport z ostatnich 3 miesięcy i bang: trzeba będzie wyciągnąć z 10 000 encji i już wszystko kwiczy.

1

Nawet jak zrobisz osobny serwis, to dojedziesz bazę. Pomysł z repliką read-only podoba mi się jako jakiś quick win, ale na pewno nie na dłuższa metę.

1

W jaki sposób joinowane?
Jeżeli masz porobione mapowania w jakimś jpa to sprawdź jak idzie ci te 10k encji.
Może zaciąga za duzo i dodatkowo później wyrzuca co nie potrzebne.

Tak jak wcześniej napisane zostało, do raportów jest replika.
Tylko, ze z replika tez jest wyzwanie. Każda obciąży ci system bazodanowy i dobrze mieć przy sobie kogoś kto doradzi. Najczęściej w IT wiedza o co chodzi.

0

@DKN: odniosę się do Twojego posta i komentarza :)

  1. Jak mówimy o raportach, to nie mówmy o encjach ani żadnym ORM. Raporty to raporty, model to model.
  2. Na dłuższa metę to kozę okazać się nie wystarczające, ponieważ zwykle pod raport potrzebujesz zdenormalizowanego modelu, jakiejś kostki, po drugie po co dojeżdżać klaster wykorzystywany przez aplikację, po trzecie można to trzymać na jakimś tanim storagu typu HDFS.
0

Czyli będę doradzał dedykowaną replikę bazy danych + wydzielony serwis tylko i wyłącznie do raportów.
@DKN no muszę te jony robić, bo nie jest to pchane prosto na twarz, ale są liczone jeszcze jakieś wartości z tych encji, a potem serializowane do określonego formatu.
Właśnie rozkminiałem jak zrobić z tych kilku tabel widoki.

Ale to muszę się bardziej podszkolić w materii baz danych, bo JPA i save(), albo findById() nie starcza :P

0

A ile w ogóle masz tych danych? Bo baza + ram całkiem dużo udźwigną

7

@DKN no muszę te jony robić, bo nie jest to pchane prosto na twarz, ale są liczone jeszcze jakieś wartości z tych encji, a potem serializowane do określonego formatu.
Właśnie rozkminiałem jak zrobić z tych kilku tabel widoki.

Ale to muszę się bardziej podszkolić w materii baz danych, bo JPA i save(), albo findById() nie starcza :P

Przepraszam, że tak wprost, ale komu Ty chcesz doradzać, skoro sam piszesz, że nie znasz się na bazach? Do tego jeszcze jakieś encje chcesz tworzyć pod raport? Przecież to kuriozum jakieś :D

5

Napisz

  • ile masz danych przechowywanych,
  • ile ich przybywa,
  • ilu ma byc jednoczesnych uzytkownikow,
  • jak swieze musza byc dane
  • ile masz czasu na wygenerowanie raportu
  • czy raport ma limit rekordow
  • ile masz ramu na wezel
  • czy uwzgledniasz rozwiazania wielowezlowe
  • jaki masz czas na odtworzenie bazy
  • ile moze byc kolumn w raporcie
  • jakiego rodzaju dane przetwarzasz? Relacyjne, dokumentowe, temporalne, grafowe...?
  • czy uzgledniasz rozwiazania chmurowe?
  • czy sa okna i szczyty dobowe?
  • jaki jest budzet miesieczny?
0

Pomijając jakieś porady doraźne to na dłuższą metę warto podejść do problemu od innej strony. Skoro potrzebujesz aby Twoje raporty (odczyt danych) nie rzutowały na prędkość głównej bazy (zapis danych) oraz musisz dostarczać odpowiedzi na jakieś złożone zapytania (jak mniemam, bo z prostymi zapytaniami raczej nie powinno być większego problemu) to warto rozważyć zastosowanie CQRS. Wtedy po stronie zapytań (Query w CQRS) możesz użyć oddzielnej bazy, ale dodatkowo możesz również tworzyć gotowe widoki pod konkretne rodzaje raportów. Innymi słowy możesz dowolnie denormalizować dane wedle potrzeb.

0

Przepraszam, ale czegoś tutaj nie rozumiem - dlaczego musisz WSZYSTKIE rekordy ładować do pamięci? Nie możesz tego robić w iteracji, pobierając z bazodanowego resultsetu rekord po rekordzie, aktualizując obiekt raportu będący instancją jakiejś klasy reprezentującej raport?

0

A ja podejdę do tematu z innej strony - zazwyczaj jak się ma aplikację webową i się widzi listę czegoś tam, to na jednej stronie jest tego 20, 50 bądź 100 - standardowy page size. Jak ktoś chce tego generować 10k rzeczy, to siłą rzeczy dużo czasu to zajmie.

A do generowania raportu polecam Jasper Reports

0

Po kolei - napisz, ile to jest dużo twoim zdaniem i jaką masz architekturę.

Zakładająm, że to zwykły MVC oraz baza danych. Bez zmian w architekturze:

  1. Batchowanie, tj. zaciągasz 100 tys. rekordów (lub jakąś inną liczbę), dorzucasz do generowanego raportu, zaciągasz kolejne 100 tys. itp. - to się sprawdzi, gdy raport zawiera jakąś ostrą redukcję, np. saldo z wszystkich transakcji. Oczywiście się trzeba trochę zastanowić jak rozwiązać problem transakcyjności, tj. gdy do bazy wpadnie rekord, który miałby trafić do raportu - ale to zostawiam tobie
  2. Stworzenie procedury bazodanowej, która taki raport stworzy i zwróci - to jest spoko gdy raport to coś więcej niż wyciągnięcie czegoś z bazy danych natomiast działa tylko i wyłącznie w zakresie wspomnianej bazy danych.
  3. Stworzenie agregatu w bazie danych - to zadziała, jeśli nie ma zbytniej opcji customizacji raportów. Oczywiście sposób agregowania zależy od ciebie - może to być widok zmaterializowany, jakiś job co noc wypełniający agregaty czy nawet nielubiane przeze mnie triggery.

Zmiany w architekturze:
4. Wspomniana replikacja bazy danych
5. Oddzielna aplikacja generująca raporty w tle tak, żeby to działało w ten sposób: użytkownik klika, że potrzebuje jakiegoś raportu - tutaj masz pełną dowolność jeśli chodzi o narzędzie. Jedyne co to musisz zaprojektować callback z jednej instancji do drugiej.

To tak na szybko, oczywiście opcji jest więcej, a wspomniane opcje można spokojnie łączyć (w ramach zdrowego rozsądku).

@Pinek: 10 tys. rekordów to jest mało i nawet niewielka JVM to udźwignie.

0

A zatem od początku.

  • Klasa POJO dla modelu rekordu, który chcę uzyskać to 23 pola Long, String, BigDecimal. Te Stringowe pola mają na oko max. 15 znaków.
  • Dane mają kilka Joinów więc to mi tą bazę rżnie właśnie, dlatego chciałem zrobić to widokiem, albo denormalizacja tabel
  • Muszę mieć to na API, do którego integrują się klienci oraz wewnętrzne administracyjne dashboardy
  • Architektura to zwykłe JPAowe repozytorium wystawione na kontroler, który zwraca strumień bajtów
  • W sumie zaskoczony jestem, że 10 tys. rekordów to mało :D Myślałem, że to bardzo dużo. W tym momencie jeden taki raport u mnie może mieć jakieś 3 tys. rekordów, z tym że ruch rośnie i muszę przygotować skalowalne rozwiązanie, które za kilka miesięcy nie wybuchnie

Wnioski z powyższych postów:

  • chcę zrobić widok, a z widoku pobierać batche, aż pobiorą się wszystkie. Co sądzicie o bieżącym zapisywaniu do pliku? Oszczędzi to ram? Np: Weź batch 500 rekordów -> zapisz do pliku -> przeleci GC -> weź kolejny batch 500 rekordów -> zapisz do pliku etc. etc.
    Ma to sens? Czy operacja zapisu jest błędogenna albo ciężka? Chociaż jednak to bez sensu, bo i tak taki plik będę musiał wczytać jako byte[], chyba że też da się zrobić tak, żeby strumień cały się nie ładował na raz do pamięci tylko ciągnął te bajty na bieżąco, może tak działa Stream API javy? :) Doczytam sobie w każdym razie.

Ogólnie zapomniałem: ta aplikacja to monolit i dlatego nie może mnie ponosić fantazja i zabierać zasobów na procesowanie raportów, jak inne corowe funkcjonalności muszą być dostępne.

0

Pracowałeś kiedykolwiek z ResultSetem?

https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html

Ustawiasz kursor na READ_ONLY oraz TYPE_FORWARD_ONLY i jedziesz rekord po rekordzie.

Nawet mając raport tworzony z 50 miliardów rekordów tym sposobem zużyjesz minimalną ilość RAMu, nie wspominając o jakichś śmiesznych 3 tysiącach rekordów LOL

Nie rozumiem po co ludzie tak strasznie chcą sobie komplikować i utrudniać życie - zrzuty do plików tekstowych, replikacje przy samym odczycie dla raportu? WTF?

0

@TomRZ: sprawa jest prosta. Komplikują sobie życie, w tym ja, bo nie mają wiedzy :) Dlatego dzięki Ci, bo ten ResultSet to chyba to czego szukam. Zabieram się za lekturę dokumentacji.
PS: Da się zrobić, żeby dla tego wątku, który właśnie zajmuje się generowaniem raportu określić górną granicę pamięci ram?

3

@NeutrinoSpinZero:

Co sądzicie o bieżącym zapisywaniu do pliku? Oszczędzi to ram?

o_O Ale po co? Mój laptop ma prawie 5 lat i ma 32GB ramu. W pracy serwery na których działa nasze oprogramowanie mają 256/512GB ramu każdy. A ty mówisz tutaj o 10k rekordów z 23 polami każde. Załóżmy że każde takie pole ma 10B, to by nam dało sumarycznie zawrotne 2.3MB a ty chcesz tu gdzieś oszczędać RAM. Załóżmy nawet że każde twoje pole ma 1000 razy więcej danych, to nadal jest raptem 2.3GB.

1

Poniższy post potraktuj (potraktujcie) jako nie znam się to się wypowiem.

Jak zrobię szybkie rozwiązanie to padną serwery i będzie na mnie.
Jak nie zrobię rozwiązania to nie będzie raportów i będzie dym i też na mnie :D
Na Hadoopa nie mam czasu, a i tak go nie umiem nic a nic.

Brzmi jak słabe skalowanie, ale nie wnikam, nie każdy ma nieograniczony Azure/AWS itp.

Bardzo mi się podobają tutaj pomysły zrobienia sobie czegoś na wzór hurtowni. Skoro robisz raporty to stawiam, że z danych, które już się nie zmieniają.
Robisz kilka/naście joinów do jednego raportu. Zamiast robić te joiny wtedy kiedy potrzebne są te raporty (a z tego co czytam nie pobiera się jednego raportu tylko jakąś ustaloną z góry liczbę) to pyk hurtownię raz dwa.
Po odpowiednim przetworzeniu danych/wykonaniu konkretnych operacji/przetworzeniu odpowiedniego eventu zebrać wszystkie potrzebne dane do kupy, władować w POJO i zapisać w osobnej bazie. Jak będzie trzeba je wyciągnąć to masz jednego SELECT'a i z głowy.
Przy takim podejściu możesz się wtedy na spokojnie zastanowić co z resztą (czyli danymi które masz w bazie ale jeszcze nie w hurtowni):

  1. wybierasz rozwiązanie lazy - jak potrzebny raport to zbierasz wszystko do kupy, zapisujesz to nowe POJO i generujesz raporcik (zauważ, że stan hurtowni tutaj jest niespójny, ja bym z tego rozwiązania nie korzystał jeśli jest taka możliwość)
  2. żeby uniknąć zajechania systemu robisz sobie coś na wzór web crawlera, tylko, że do swoich danych - przechodzisz sobie po kolei przez jakieś tam kolejne ID/daty/cholera_wie_co i generujesz po kolei te POJO, ale z umiarem i kilku/kilkunasto sekundową przerwą, żeby systemy miały wystarczająco czasu przerobić pozostałe zapytania. Ba, możesz nawet osobny serwisik sobie do tego stworzyć, ustawić, żeby orał po bazie tylko w godzinach małego load'u i za parę nocy będziesz się śmiał przy piwku, że problem się sam rozwiązał i to małym kosztem.

Jak tych joinów jest rzeczywiście dużo to bardziej obeznani koledzy polecą jakieś rozwiązania. Zawsze jest JdbcTemplate ze Springa, który może pomóc trochę zredukować narzut JPA/Hibernate/Spring Data. Daj znać co tam wykminiłeś, lubię takie problemy :P

Architektura to zwykłe JPAowe repozytorium wystawione na kontroler, który zwraca strumień bajtów - encja na twarz i pchasz, nieładnie

P.S. Nie znam szczegółów, mogę doradzać źle, przepuść moją opinię przez sito. A, no i nigdy nie pracowałem na Oracle DB.

0

10k rekordów to możesz obrobić nawet w Excelu (pobierając dane przez Query).
Jakbyś miał dane pow. 65k wierszy, to poczytaj to:
https://dzone.com/articles/50-best-performance-practices-for-hibernate-5-amp

1

Hmm RAM ramem, ale co z operacjami IO? Przecież taki raport musi być albo JSONem (serializacja 10 000 obiektów trochę zajmie czasu), albo pdfem (wtedy zapis do pliku który będzie mieć ~~300 stron też chyba nie pójdzie szybko)

3

IMO jeśli to większy plik to powinien być wygenerowany asynchronicznie, zapisany na dysku a później pobrany kolejnym requestem czy coś w tym stylu

0

Do tego typu problemów warto zainteresować się takimi rozwiązaniami jak HDFS lub Cassandra (jako storage) i Apache Flink (przetwarzanie danych). Flink pozwala zarówno na ciągłe przetwarzanie i jednorazowe przetwarzanie zdefiniowanego zbioru danych (Twój przypadek). Masz tam też gotowe connectory do różnych źródeł danych (np. Cassandra).

1 użytkowników online, w tym zalogowanych: 0, gości: 1