Narzędzia użytkownika

Narzędzia witryny


r_wydajnosc

To jest stara wersja strony!


Jak rozsądnie dzielić przetwarzanie danych między bazę danych i własny komputer?

Wstęp

Używany przez funkcje pakietu ZPD ułatwiające pobieranie danych z bazy pakiet dplyr umożliwia samodzielne decydowanie przez użytkownika o tym, jakie czynności wykonane zostaną:

  • po stronie bazy danych (po stronie serwera);
  • lokalnie na jego komputerze (po stronie klienta).

Odbywa to się w następujący sposób:

  • wszystkie funkcje pakietu ZPD pobierające dane z bazy tak naprawdę nie pobierają danych, a jedynie zwracają obiekt dplyr-a, który wie, jak te dane pobrać;
    • na obiekcie tym można stosować dowolne czasowniki dplyr-a, np. filtrujące dane, tworzące nowe zmienne, wykonujące złączenia, itd.;
  • fizyczne pobranie danych z bazy (z serwera) następuje dopiero w momencie wykonania czasownika collect();
    • collect() jest też wykonywane zawsze, kiedy obiekt dplyr-a konwertowany jest na zwykłą R-ową ramkę danych, np. za pomocą as.data.frame();
  • przetwarzanie danych po wykonaniu collect() odbywa się już lokalnie na komputerze klienta.

Przy pobieraniu dużych ilości danych z bazy to, w którym momencie dane zostaną fizycznie przesłane na komputer klienta (w którym momencie wywołane zostanie collect()) może mieć duże znaczenie dla całkowitego czasu, jaki zajmie operacja pobierania, filtrowania i złączania danych. Najważniejsze czynniki to:

  • Redundancji (stopnia duplikacji) pobieranych danych

    • Przykładem redundancji mogą być:
      • pobieranie PV (grupa danych oszacowania) z dołączonymi informacjami o szkołach - wszystkie PV wszystkich uczniów uczęszczających do tej samej szkoły będą posiadać dokładnie te same wartości zmiennych opisujących szkołę, wartości te będą więc zduplikowane (dla n PV i m uczniów w szkole n * m razy);
      • pobieranie wyników egzaminacyjnych dla wszystkich przedmiotów maturalnych w danym roku z dołączonymi informacjami o uczniach - ta sama informacja o uczniu będzie powtórzona tyle razy, do ilu przedmiotów na maturze podszedł dany uczeń.

    • Dane w bazie danych przechowywane są w postaci relacyjnej, która jest nieredundantna - wytworzenie złączonego redundantnego zbioru danych zajmuje serwerowi czas;
      • ze względu na ograniczone zasoby serwera baz danych, nie jest on w stanie w wypadku większych danych (np. wyników egzaminacyjnych czy oszacowań umiejętności uczniów) zapamiętać ostatnio dołączanych danych w pamięci operacyjnej i następnym razem (np. dla kolejnych przedmiotów maturalnych) wykonać złączenia szybciej;
      • w takim wypadku za każdym razem musi on ponownie odczytywać wszystkie dane z dysku, co zajmuje dużo czasu;
      • z drugiej strony serwer baz danych jest w stanie przetwarzać tablice danych o rozmiarach przekraczających rozmiar pamięci operacyjnej serwera (jest to dość powolne, ale działa stabilnie).

    • Jeśli te same dane zmieściłyby się w pamięci operacyjnej komputera klienta, mógłby on dokonać ich złączenia szybciej;
      • z drugiej strony w R (generalnie) nie jest możliwa obróbka danych większych niż pamięć operacyjna komputera, a więc rozwiązanie to jest możliwe tylko dla zbiorów danych o ograniczonym rozmiarze.

  • Ilości danych przesyłanych z serwera bazy danych

    • Zbiory danych wyników egzaminacyjnych i/lub oszacowań uczniów mogą mieć rozmiary dochodzące do kilkudziesięciu megabajtów.
    • Dołączanie do nich redundantnych zmiennych kontekstowych może zwiększyć te rozmiary nawet kilkukrotnie.

  • Obciążenie serwera

    • jeśli baza danych jest w danej chwili obciążona wieloma zapytaniami, wtedy szybkość przetwarzania danych po stronie serwera ulega znacznemu ograniczeniu i szybsze może okazać się przerzucenie większej ilości pracy na własny komputer;
    • aktualne obciążenie bazy danych sprawdzić można pod adresem http://zpd.ibe.edu.pl/doku.php?id=baza_status .

Wskazówki

Pobieraj tylko te zmienne, których potrzebujesz

  • Jeśli nie potrzebujesz jakichś zmiennych z danej grupy danych, wtedy ich nie pobieraj.
    • Wybierz tylko te zmienne, których potrzebujesz, czasownikiem select().
    • Zachowaj także te zmienne, których będziesz potrzebować do ew. złączeń z innymi danymi w późniejszym czasie.

  • W szczególności dotyczy to:
    • grupy danych szkoły - zawiera ona bardzo dużo zmiennych i zapewne nie będziesz korzystać ze wszystkich;
    • grup danych, które dołączasz tylko po to, by odfiltrować dane (np. wyniki egzaminacyjne tylko z pewnego obszaru Polski).

Przyspieszy to zarówno przetwarzanie danych po stronie serwera bazy danych, jak i na Twoim komputerze.

Przykład

Potrzebujemy zmiennej wojewodztwo z grupy danych szkoły aby odfiltrować wyniki egzaminacyjne (sprawdzian 2012 z danych CKE) tylko do obszaru województwa mazowieckiego.

Dobrze:

library(ZPD)
src = polacz()
 
### Patrz tutaj:
szkoly = pobierz_szkoly(src) %>% select(id_szkoly, rok, wojewodztwo)
 
s_12 = pobierz_wyniki_egzaminu(src, 'sprawdzian', '', 2012, F)
s_12 = inner_join(s_12, szkoly)
s_12 = s_12 %>% collect()
save(s_12, file = 's_12.RData')

Źle:

library(ZPD)
src = polacz()
 
### Patrz tutaj:
szkoly = pobierz_szkoly(src)
 
s_12 = pobierz_wyniki_egzaminu(src, 'sprawdzian', '', 2012, FALSE)
s_12 = inner_join(s_12, szkoly)
s_12 = s_12 %>% collect()
save(s_12, file = 's_12.RData')

Filtruj dane tak wcześnie, jak to możliwe

  • Odfiltrowanie danych po stronie serwera nie wpływa niekorzystnie na czas pobrania danych z serwera, ogranicza natomiast rozmiar danych przesyłanych przez internet.

  • Wszystkie operacje przekształcania danych (złączanie, grupowanie, tworzenie nowych zmiennych, itd.) wykonują się szybciej na mniejszych danych - zarówno po stronie serwera, jak i po stronie klienta.

  • Złączanie inner_join z odfiltrowaną grupą danych również filtruje złączane dane.
    • np. jeśli chcemy pobrać wyniki egzaminacyjne tylko dla wybranych województw, wtedy efektywnie będzie złączyć je złączeniem inner_join jeszcze po stronie serwera z ograniczoną do zmiennych id_szkoly, rok i wojewodztwo (patrz poprzednia wskazówka) grupą danych szkoły.

Przykład

Chcemy pobrać oszacowania EAP umiejętności uczniów dla skali 728 i skalowania 31002 (zrównane wyniki egzaminu maturalnego z matematyki na poziomie podstawowym):

Dobrze:

library(ZPD)
src = polacz()
 
eap_m_mat_p = pobierz_oszacowania(src) %>% filter(estymacja == 'EAP', id_skali == 728, skalowanie == 31002)
eap_m_mat_p = eap_m_mat_p %>% collect()
save(eap_m_mat_p, file = 'eap_m_mat_p.RData')

Źle:

library(ZPD)
src = polacz()
 
eap_m_mat_p = pobierz_oszacowania(src)
eap_m_mat_p = eap_m_mat_p %>% collect()
eap_m_mat_p = eap_m_mat_p %>% filter(estymacja == 'EAP', id_skali == 728, skalowanie == 31002)
save(eap_m_mat_p, file = 'eap_m_mat_p.RData')

Jeśli zmienne kontekstowe wprowadzają znaczną redundancję, pobierz je osobno i złącz po stronie klienta

Jeśli dołączasz te same dane kontekstowe do wielu grup danych

  • np. dane o uczniach i/lub szkołach do wyników egzaminacyjnych wszystkich przedmiotów maturalnych w danym roku albo wszystkich części egzaminu gimnazjalnego w danym roku;

wtedy:

  • Osobno pobierz na swój komputer zbiór danych kontekstowych.
    • pamiętaj o tym, aby (w miarę możliwości) je odfiltrować (patrz poprzednia wskazówka);
  • Osobno pobierz na swój komputer dane, do których będziesz je dołączał.
  • Połącz dane u siebie na komputerze (po stronie klienta).

Uwaga! Jeśli dane kontekstowe potrzebne są do odfiltrowania danych, z którymi będą złączane (np. pobieramy wyniki egzaminacyjne tylko dla pewnego regionu), wtedy trudno jednoznacznie rozstrzygnąć, co przyniesie większy zysk:

  • czy wczesne odfiltrowanie danych po stronie serwera uzyskane dzięki złączeniu po stronie serwera z odfiltrowanymi danymi kontekstowymi;
  • czy uniknięcie redundancji danych pobieranych z serwera.

Przykład

Pobieramy wyniki egzaminu maturalnego z wszystkich przedmiotów dla roku 2011 dołączając dane o szkołach.

Dobrze:

library(ZPD)
src = polacz()
 
przedmioty = c("biologia", "chemia", "fizyka", "geografia", "historia", "informatyka", "j. polski", "matematyka", "WOS")
wyniki = list()
for(przedmiot in przedmioty){
  for(poziom in c('podstawowa', 'rozszerzona'){
    czescEgzaminu = paste(przedmiot, poziom)
    tmp = pobierz_wyniki_egzaminu(src, 'matura', czescEgzaminu, 2011, T)
    wyniki[[czescEgzaminu]] = tmp %>% collect()
    save(wyniki, file = 'matura_2011.RData')
  }
}
 
szkoly = pobierz_szkoly(src) %>% filter(rok == 2011) %>% collect()
wyniki = sapply(wyniki, function(x)){
  return(inner_join(x, szkoly))
}
save(wyniki, file = 'matura_2011.RData')

Źle:

library(ZPD)
src = polacz()
 
szkoly = pobierz_szkoly(src) %>% filter(rok == 2011)
 
przedmioty = c("biologia", "chemia", "fizyka", "geografia", "historia", "informatyka", "j. polski", "matematyka", "WOS")
wyniki = list()
for(przedmiot in przedmioty){
  for(poziom in c('podstawowa', 'rozszerzona'){
    czescEgzaminu = paste(przedmiot, poziom)
    tmp = pobierz_wyniki_egzaminu(src, 'matura', czescEgzaminu, 2011, T) %>% collect()
    tmp = inner_join(tmp, szkoly)
    wyniki[[czescEgzaminu]] = tmp %>% collect()
    save(wyniki, file = 'matura_2011.RData')
  }
}

Podziel pobieranie większych ilości danych na części

  • Unikaj konstruowania takich zbiorów danych, których pobranie z serwera trwałoby bardzo długo.
    • Wraz z przedłużającym się czasem wykonywania pobrania danych z serwera rośnie ryzyko, że może ono zostać przerwane, np. przez:
      • (choćby chwilową) utratę połączenia z internetem;
      • awarię po stronie serwera.
    • Pobieranie danych trwające dłużej niż godzinę z całą pewnością wyczerpuje definicję bardzo długo.

  • W takim wypadku podziel pobranie danych na części, np.

Przykład

Chcemy pobrać PV ze zrównywania części humanistycznej egzaminu gimnazjalnego z lat 2002-2014 (skala 854, skalowanie 22003).

Dobrze:

library(ZPD)
src = polacz()
 
wyniki = list()
pv = pobierz_oszacowania(src) %>% filter(estymacja == 'PV', id_skali == 854, skalowanie == 22003)
for(i in 2002:2014){
  wyniki[[as.character(i)]] = pv %>% filter(rok == i) %>% collect()
  save(wyniki, file = 'pv_gh_02-14.RData')
}

Źle:

library(ZPD)
src = polacz()
 
pv = pobierz_oszacowania(src) %>% filter(estymacja == 'PV', id_skali == 854, skalowanie == 22003)
pv = pv %>% collect()
 
save(pv, file = 'pv_gh_02-14.RData')
r_wydajnosc.1443373370.txt.gz · ostatnio zmienione: 2015/09/27 19:02 przez zozlak