Aplikacja wyborcza jako strona internetowa. Wszyscy użytkownicy mają móc czytać wyniki wyborów w kraju, województwie, okręgu, powiecie, gminie, obwodzie. Zalogowani mają móc je zmieniać. Zrobić w Django, dane trzymać w bazie danych.
Oczywiście zadanie akademickie. Ale ono sprawiło, że zacząłem się zastanawiać – po co w ogóle używać bazy danych, jeśli dane zmieszczą się w RAMie? (a tu się mieszczą bez problemu).
Podobno serwery WWW spędzają zazwyczaj ~90% czasu na czekaniu na IO. Czyli, m.in, na bazach danych. Zatem byłoby wskazane je optymizować. Tymczasem baza danych oferuje nam: • Czytanie z dysku pesymistycznie za każdym jednym odczytem; • pesymistycznie liniową złożoność zapytania o jeden wiersz dowolnej tabeli!
Załóżmy np, że mamy obwód i chcemy wiedzieć, w jakiej znajduje się gminie (czyli przechodzimy do klucza obcego). Złożoność: O(liczba gmin), bo trzeba przejść się po tabeli z gminami: SELECT * FROM gmina WHERE kod_gminy = obwód.kod_gminy
A powinno być O(1). Załóżmy, że mamy gminę i chcemy wiedzieć, jakie znajdują się w niej obwody. Złożoność: O(liczba obwodów w kraju), bo trzeba przejść się po tabeli z obwodami: SELECT * FROM obwód WHERE kod_gminy = gmina.kod_gminy
A powinno być: O(liczba obwodów w gminie).
Dwie kontrpropozycje do baz danych:
- Program w C lub C++ trzymający te dane w RAMie i odpowiadający na zapytania serwera WWW jakby był NoSQLowoą bazą danych. Np. mając obwód pytanie o gminę jest tutaj trywialne: miast SQLowego przeglądania tabeli z gminami przechodzimy po wskaźniku:
class Obwód {
/* ... */
Gmina *gmina;
/* ... */
public:
/* ... */
Gmina &weź_gminę() {
return *gmina;
}
/* ... */
};
Zapytanie o obwody w gminie:
class Gmina {
/* ... */
std::vector<Obwód*> obwody;
/* ... */
public:
/* ... */
std::vector<Obwód*> const& weź_obwody() {
return obwody;
}
/* ... */
};
Jeśli obwody (obiekty, nie wskaźniki) trzymane są w tablicy posortowanej gminami, to można nawet tak: std::vector<Obwód>::const_iterator obwody_początek, obwody_koniec
Oczywiście pojawia się problem że dane muszą przetrwać restart maszyny. Świetnie zatem: przy każdym zapisie serializujemy zmieniony obiekt do formatu binarnego i zapisujemy go na dysk. Albo: Co godzinę i na żądanie serializujemy i zapisujemy na dysk wszystko.
- Jeśli nie wystarczy RAMu: Trzeba wszystko trzymać na dysku i czytać z dysku. Ale tutaj chyba czysty system plików będzie wydajniejszy. Mamy np. folder
Obwody
i w nim jeden plik na obwód. W tym pliku ścieżka do pliku z gminą (a więc wzięcie gminy: znów O(1)). I w folderzeGminy
każdy plik z gminą będzie zawierał ścieżki do plików z odnośnymi obwodami.
Przyznaję, benchmarków jeszcze nie robiłem, ale odnoszę być może błędne wrażenie, że każde z tych rozwiązań (a już w szczególności to pierwsze) będzie szybsze od bazy danych.
Czemu więc jednak używa się baz danych?