Duża ilość danych do pobrania i zapisania

Witam!
Napisałem program pobierający bazę danych z pewnego słownika. Zanim go uruchomię (a wykonanie programu zajmie kilka godzin) muszę być pewien, że w trakcie nic się nie wywali z powodu dużej ilości danych.

Najpierw pobieram wszystkie hasła do tablicy. Jest ich 190 tysięcy.
Potem do hashu o kluczu: “hasło” i wartości: “definicja” pobieram definicje haseł. Jest ich około 600 tysięcy (definicji).
Następnie dane te konwertuję do JSON i zapisuję do pliku.

Czy powyższy program nie zablokuje się np. z powodu “przeładowania pamięci tablicy”, albo wielkość pliku będzie za duża, żeby ją zapisać? Chcę uniknąć z góry takich problemów, a jest to kwestia czysto teoretyczna, którą cieżko sprawdzić w praktyce.
Pracuję na Windowsx64

Ekhm? ; d. Na porządku dziennym wgrywam dumpy z 4kk+ rekordami i wszystko działa świetnie. Ruby i Windows?

Dobra, no po prostu nie byłem pewien, jak to wygląda :smile:

I tak, Ruby i Windows. Normalnie korzystam z Windowsa, ze względu na inne programy, a po formacie jeszcze nie instalowałem wirtualki z Fedorą :smile:

Hola hola. Co innego wgrywać dumpa (jak rozumiem do bazy) a co innego w procesie rubiego pobierać 600 tysięcy rekordów. Przy takiej ilości jest wielce prawdopodobne, że zabraknie pamięci.

Dobra, odpaliłem to. Najwyżej stracę kilka godzin :smiley: Dam znać, jak poszło i jaka jest wielkość pliku końcowego, jeśli się wszystko dobrze wykona.

Słowa już się zaindeksowały, a definicji jest pobrane 7.3%.

EDIT:
Ok, przy 11.6% wyrzuciło niemożność wykonania metody .inner_html na elemencie NIL, co by oznaczało, że się nie pobrał. Prawdopodobnie… Podzieliłem więc wszystko na 10 wykonań skryptu i właśnie leci pierwsze. Jeśli pierwsze się wykona, a drugie nie, to błąd nie leży w pamięci i wtedy zacznę szukać wadliwego wpisu.

EDIT2:
Dobra, błąd leży po stronie odbieranych danych.

Ja się tylko zastanawiam po co to robić? Wczytywanie tak dużego pliku JSON będzie trawło wieki.
Czy nie lepiej użyć zwykłej bazy relacyjnej albo nosqlowej? Jeśli dane byłyby jakoś bardzo powiązane wewnętrznie, to polecam moją Ruby Object Database http://github.com/apohllo/rod, którą stworzyłem właśnie do danych słownikowych. Ewentualnie można rozważyć użycie gemu do BerkleyDB (o ile coś aktualnego jest dostępne, bo kiepsko z tym bywało).

Jeśli przedstawisz szerzej zastosowanie, to mogę Ci coś doradzić, bo siedzę w temacie kilka lat.

No wlasnie - to typowe zastosowanie do klucz/wartosc serializowanego na dysk. Perl do czegos takiego dawal ‘tied hashes’ - prosta opakowywarka na hash-a tak, by operacje czytaj/zapisz trafialy z/na dysk. Jakies 15 lat temu :wink:

Scenariusz chyba nie jest aż tak prosty, bo na 190 tys. kluczy jest 600 tys. definicji, więc nie jest to 1-do-1. Ale tak czy owak fakt, że Perl w tym wypadku przewyższa Rubiego jest trochę niewygodny :wink:

Znaczy przerzuciłem się na MongoDB i wszystko wydaje się prostsze, ale z różnych powodów nie umiem tego ukończyć - skrypt działa kilka godzin i zawsze jakiś inny błąd. Wszystko staram się naprawiać na bieżąco… ostatni błąd to (już po przerzuceniu się na MongoDB) end of file - i jeszcze nie rozgryzłem, o co chodzi :smiley:

mongodb to mam nadzieję wersja 64 bitowa? :wink:

Wynikowa baza powinna mieć około 100MB. Czy 64-bitowa wersja w takim przypadku coś zmienia…?

http://docs.mongodb.org/manual/reference/limits/

W takim razie rozbiję to na kilka kolekcji i tyle… Już tracę siły do tego :grin:

Otocz krok pętli kodem:

log = File.open("path/to/log.txt","a")
# główna pętla
  begin
    # krok pętli
  rescue Interrupt
    break
  rescue Exception => ex
    log.puts ex
    log.puts ex.backtrace
  end
end
log.close

Dodatkowo możesz użyć gemu progress żeby monitorować ile czasu pozostało do końca zadania.
Warto też zainstalować sobie htop z stracem, żeby monitorować ile pamięci jest zajętej oraz jakie polecenie systemowe są wywoływane przez działający proces. Oczywiście monitorowanie logu mongo też nie jest bez znaczenia.

Ale jak zamierzasz potem szukać definicji? W każdej kolekcji z osobna? To trochę bez sensu. Użyj innego narzędzia.
A co do ramu, to fakt, że kolekcja na dysku ma 100MB nie oznacza, że 2GB ramu są wystarczające. Możesz mieć memory leak w samym skrypcie Rubiego. Polecałbym też użycie Rubiniusa, który znacznie lepiej zarządza pamięcią niż MRI.

Na razie najważniejsze dla mnie jest to po prostu pobrać. Jak już mi się to uda pobrać, to przepiszę na plik i ten plik poprzez PHP zapiszę sobie już w bazie MySQL - której chciałem użyć od samego początku, ale gem nie chciał współpracować z Ruby 2.0 na Windowsie… Bardziej chciałem po prostu sobie poćwiczyć coś :grin:

Mimo to dzięki za rady, już sobie wszystko zapisałem i wykorzystam je w przyszłych programach.

Jakiego gemu używasz? mysql czy mysql2? Przy tej ilości danych również sqlite dałby radę. Ale wydajność Berkeley DB jest i tak znacznie wyższa.

sqlite nie chce działać z Ruby 2.0, masa tematów na ten temat w Google jest.
mysql i mysql2 próbowałem, miałem masę problemów z kompilacją tak, żeby działało wszystko i dlatego przerzuciłem się na MongoDB.

Co do Rubiniusa - w przypadku Ruby wychodzenie w Windowsie poza domyślne, najłatwiej dostępne narzędzia, jest dalece nieoptymalne: wysiłek jest nieadekwatny do korzyści. Jeśli będę coś poważniejszego programował w Rubim, to zainstaluję - tak jak kiedyś - wirtualną maszynę z GNU/Linuxem. Na tę jednak chwilę zwyczajnie nie jest mi to potrzebne.

myślę, że większość Twoich problemów wynika z tego, że próbujesz robić to pod Windowsem.

1 Like