Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Baza danych do prowadznia statystyk wyświetleń.
Forum PHP.pl > Forum > Bazy danych
Vanitas
Witam, zaprojektowałem tabelę w bazie danych do prowadzenia statystyk wyświetleń strony, tabela wygląda następująco:
Kod
+---------+--------------+---------+--------------+---------+
|   id    |     ip       |  time   |  user_agent  | count   |
| int(11) | varchar(255) | int(11) | varchar(255) | int(11) |
+---------+--------------+---------+--------------+---------+

gdzie time przechowuje czas odwiedzin (unix timestamp) a count jest inkrementowane, jeśli ten sam IP odwiedza stronę w tym jednym przedziale czsowym. Każdy przedział czasowy ma długość jednej godziny, np. 11:30 - 12:30.
Teraz jeśli chcę pobrać dzienną sumę nieunikalnych wyświelteń:
  1. SELECT SUM(count) AS views FROM `log_views` WHERE time >= POCZATEK_DNIA AND time < POCZATEK_DNIA + 86400

W przypadku, kiedy chcę wykonać godzinowe zestawienie z danego dnia, muszę wykonać 24 analogiczne zapytania, dla każdego przedziału czasowego jedno. W przypadku zestawienia miesięcznego 31 (zależy...) zapytań dla każdego dnia w miesiącu.
Po jakimś czasie danych zacznie przybywać (nie żeby jakoś ekstremalnie szybo rolleyes.gif ). Czy powinienem zastosować dodatkowe tabele ze zbiorczymi statystykami z dnia / tygodnia / miesiąca?
Muszę też dodać, że z tych statystyk korzystać bedzie tylko jeden / kilku administratorów i sądzę, że nie będą siedzieć i odświeżać strony wyświetlającej w.w. statystyki bez końca (i podziwiać wykresów z google chart api).
Mam jeszcze jedno pytanie. W jaki sposób rośnie czas dostępu do danych w MySql'u w zależności od ilości danych?
gothye
pole IP zmień na INT(11) a sam adres umieszczaj wykorzystując funkcję Mysql : INET_ATON
zmniejszysz wielkość indeksu oraz samą objętość tabeli ,

pole user_agent również zmień na int(11) , umieszczaj dane w niej w sposób :

sprintf("%u",crc32(md5($useragent)));

Cytat
Mam jeszcze jedno pytanie. W jaki sposób rośnie czas dostępu do danych w MySql'u w zależności od ilości danych?


w zależności od indeksów tabel
Vanitas
Dziękuję za odpowiedź - zastosowałem się do rad smile.gif
A czy zastosowanie oddzielnych tabel dla ogólnych zestawień dania / tygodnia / miesiąca ma jakiś sens?
Jedyna różnica był by taka, że zamiast tylko do bardzo szczegółowej tabeli, która teoretycznie może się rozrosnąć do dowolnych rozmiarów, miałbym zestawienia danych ogólnych dla danych okresów, których rozmiar jest z góry znany.
Tabela ogólna mogła by być czyszczona co miesiąc z danych starszych niż dwa tygodnie...
Przy wyświetleniu strony, bazując na danych z tabeli szczegółowej, zwiększył bym liczniki tabel ogólnych (analogiczne mógłbym stworzyć tabelę "rankingową" dla user agent'ów).
gothye
wszystko zależy ile danych przewidujesz ,ale wg. powyższych rad mam tabele z rekordami 10-20mln i statystyki pobierane są szybko
Bags_Bunny
W kwestii dostosowywania typów danych, ustaw polom numerycznym unsigned. Ewentualnie, zmień varchar na char.
Ale przede wszystkim, stwórz index na pole time!
Crozin
Cytat
pole IP zmień na INT(11) a sam adres umieszczaj wykorzystując funkcję Mysql : INET_ATON
Niezbyt dobra rada - patrz: IPv6.
Cytat
Mam jeszcze jedno pytanie. W jaki sposób rośnie czas dostępu do danych w MySql'u w zależności od ilości danych?
Jak już @gothye zauważył zależne jest to od użytego indeksu, jednak można powiedzieć, że czas dostępu zmienia się jakoś zauważalnie co rząd wielkości ilości danych.
Vanitas
Cytat(Bags_Bunny @ 4.03.2012, 01:25:53 ) *
Ale przede wszystkim, stwórz index na pole time!

No faktycznie, to przeoczyłem...
Cytat(Crozin @ 4.03.2012, 03:40:23 ) *
Niezbyt dobra rada - patrz: IPv6.

Na stackoverflow znalazłem dobre obejście po stronie aplikacji:
  1. //ip -> int
  2. sprintf("%u", ip2long($ip));
  3. //int -> ip
  4. long2ip(sprintf("%d", $model->ip));

Według powyższych rad, tabela wygląda tak:
  1. CREATE TABLE IF NOT EXISTS `log_views` (
  2. `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  3. `ip` int(11) UNSIGNED NOT NULL,
  4. `time` int(11) UNSIGNED NOT NULL,
  5. `user_agent` int(11) UNSIGNED NOT NULL,
  6. `count` int(11) UNSIGNED NOT NULL,
  7. PRIMARY KEY (`id`),
  8. KEY `time` (`time`)
  9. ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Przy wykorzystaniu phpBrowscap, mogę z useragent'a wydzielić system operacyjny i przeglądarkę, takie dane będą łatwiejsze do generowania statystyk względem tych danych. Dodałem także rererrer'a
  1. CREATE TABLE IF NOT EXISTS `log_views` (
  2. `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  3. `ip` int(11) UNSIGNED NOT NULL,
  4. `time` int(11) UNSIGNED NOT NULL,
  5. `browser` char(10) NOT NULL,
  6. `platform` char(10) NOT NULL,
  7. `referrer` char(20),
  8. `count` int(11) UNSIGNED NOT NULL,
  9. PRIMARY KEY (`id`),
  10. KEY `ip` (`ip`)
  11. KEY `time` (`time`)
  12. KEY `browser` (`browser`)
  13. KEY `platform` (`platform`)
  14. KEY `referrer` (`referrer`)
  15. ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Nowe dane przechowuję jako char. Co więcej, chcę dodać zadanie cron, które codziennie będzie robiło porzątki w tabeli szczegółowej, sumując dane do tabel miesięcznych / godzinowych.
Porzątki będą robione z tego względu, że przy każdej wizycie na stronie zostaje wykonane zapytanie sprawdzające, czy należy zwiększyć wartość `count` dla danego IP czy stworzyć nowy wiersz. Chcę zminimalizować czas ładowania strony przez zminimalizowanie ilości danych w tej tabeli.
Crozin
1. Zamiana IP na liczbę całkowitą jest możliwa tylko w przypadku IPv4. Na dobrą sprawę jest to odpowiednik metody @gothye, tyle że operacja zamiany jest przeprowadzona po stronie PHP, nie MySQL. Co do samego przechowywania adresu, MySQL nie jest dostatecznie pomocny, ale da się to osiągnąć: http://stackoverflow.com/questions/420680/...tional-database - może czas na przesiadkę na Postgresa? wink.gif
2. Zarówno URL z nagłówka Refferer, jak i nagłówek User-Agent są nieco dłuższe niż 10 znaków.
3. Tam gdzie to nie jest potrzebne nie używaj UNSIGNED (patrz: id, count). Mieszanie typów ze i bez znaku może spraswiać problemy, a gdybyś spodziewał się przekroczenia zasięgu danego typu i tak powinno się go zamienić na obszerniejszy niż zmieniać na typ bez znaku.
4. Ten tygodniowy/miesięczny cache lepiej by było rozwiązać za pomocą "klasycznego" cache (pliki/pamięć) bądź widoków w bazie danych zamiast tabeli.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.