Przechowywanie dużej ilości statycznych danych na potrzeby aplikacji

0

Prototypuję w pracy pewną funkcjonalność i jestem ciekaw Waszej opinii na ten temat. Nieco uproszczę problem na potrzeby dyskusji.

Dostałem na wstępie 2 pliki tekstowe z danymi:

  1. lista rekordów - każdy rekord ma kilka kolumn z danymi, w tym kolumna index (~500 000 rekordów)
  2. macierz liczb całkowitych, która potem w pamięci i tak jest reprezentowana za pomocą zwykłej jednowymiarowej tablicy integerów/shortów (~50 000 000 elementów w tablicy)

Użytkownik przy użyciu API robi wyszukiwanie wśród rekordów, czego rezultatem są 2 rekordy, a właściwie ich indexy. Na podstawie tych dwóch indexów wyliczamy pozycję w tablicy powstałej z drugiego pliku i zwracamy wartość na tej pozycji.

Charakterystyka tych danych:

  • dane są trywialne - jak widać są to proste rekordy i do tego tablica liczb całkowitych
  • dane są statyczne - nie będą zmieniane wcale, bądź bardzo rzadko (raz na rok, dwa?)
  • dane nie są poufne

Naturalnym podejściem byłoby po prostu wrzucenie tych danych do bazy SQL flywayem i elo, ale od początku ten pomysł wydawał mi się średni (wielkość plików sql, jednokrotna, choć długa inicjalizacja itp). Po charakterze danych widać, że nie różnią się one wiele od typowego pliczku z konfiguracją springa w .yml, więc spokojnie mogłyby one być przechowywane w obrębie aplikacji, lecz problemem jest ich ilość. Powiedzmy, że w przypadku rekordów jeszcze widzę sens zapisu ich do bazy, ale wtedy rozdzieliłbym powiązane ze sobą dane na 2 różne data source'y, co wydaje mi się takie sobie na dłuższą metę.

Problem jest głównie z tą dużą tablicą, więc skupiłem się na niej. Zaczytałem więc macierz z pliku .txt, przerobiłem na tablicę shortów, zserializowałem ją za pomocą kryo do pliku binarnego, który to plik chciałbym trzymać w resource'ach razem z kodem i deserializować tablicę przy starcie aplikacji. Plik zajmuje ~100 MB, czyli dużo mniej niż .txt, ale mogłoby być lepiej. Deserializacja tablicy trwa ~100ms - miodzio. Chyba najbardziej mi żal dodatkowych ~100MB na heapie, czyli w praktyce 2x tego, co mamy teraz na aplikacji w idle. Zastanawiam się też jak by to wpłynęło na GC. Trzymanie tego off-heap by pomogło, ale nadal nie zmniejszyło zużycia pamięci.

Główne zalety tego podejścia:

  • turbo wydajność
  • łatwość implementacji
  • dane są od razu w paczce razem z kodem

Wady:

  • objętość jarki się zwiększy o te 100MB - ostatecznie może zaboleć przy każdorazowym budowaniu obrazu, przesyłaniu po sieci w czasie CI jobów itp
  • powiększenie heapa o 100MB oraz być może wydłużenie gc (?)

W tym momencie testuję podejście z wykorzystanie H2 (a konkretnie mvstore). Czyli zbuduję sobie plik z bazą, który też będę trzymał z kodem i nie będę zmuszony do zaczytywania całej tablicy do pamięci. Wydajność minimalnie ucierpi, heap nie powiększy się wcale, ale za to plik z bazą w tym momencie zajmuje mi ponad 200 MB, więc tragedia :/. Choć istnieje też opcja, żeby ten plik trzymać od razu w obrazie dockerowym, zamiast w resource'ach kodu aplikacji - do przemyślenia.

Jak Wy to widzicie, może da się jeszcze jakoś zoptymalizować pierwsze podejście? A może jednak stara dobra baza relacyjna?

Aplikacja pisana w kotlinie, ale nie ma to wielkiego znaczenia.

3

Ta serializacja, a to bardzo oczywista rzecz. Proste api, mniej komponentów do zepsucia. Normalka.
#DROP DB

Jeśli chodzi o gc to będzie to w old - i nie powinieneś odczuć problemu.

0

powiększenie heapa o 100MB oraz być może wydłużenie gc (?)

Chlopie Ty przybywasz z 1990 roku z jakas Amiga?
Przeciez takie 100mb RAMu teraz to nic, laptopy developerskie maja teraz w standardzie po 32 GB RAMu. Slack Ci więcej żre pewnie xD

2

Jw. wczytujesz taki plik do mapy i po temacie. Super szybkie i bezawaryjne rozwiazanie, a przede wszystkim tanie w utrzymaniu i roboczogodzinach :) Latwo tez potem przejsc na jakas baze, jesli bedzie potrzeba.

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