Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Optymalizacja zapytań
Forum PHP.pl > Forum > Bazy danych > MySQL
sebekzosw
Mam tabelę, w której znajduje się ponad 2.500.000 rekordów.

jeżeli wykonuje zapytanie sprawdzające źródła odwiedzin:

  1. SELECT `domain`, COUNT(`id`) AS `count` FROM `all_cms_entry` GROUP BY `domain` ORDER BY `count` DESC


to trochę długo się wykonuje:

Pokaż wiersze 0 - 3 ( 4 wszystkich, Wykonanie zapytania trwało 8.5397 sekund(y))

Oto moja struktura bazy danych:
  1. CREATE TABLE IF NOT EXISTS `all_cms_entry` (
  2. `id` mediumint(8) UNSIGNED NOT NULL AUTO_INCREMENT,
  3. `page` varchar(50) COLLATE utf8_polish_ci DEFAULT NULL,
  4. `back` enum('0','1') COLLATE utf8_polish_ci DEFAULT '0',
  5. `domain` varchar(70) COLLATE utf8_polish_ci DEFAULT NULL,
  6. `referer` varchar(100) COLLATE utf8_polish_ci DEFAULT NULL,
  7. `browser` varchar(30) COLLATE utf8_polish_ci DEFAULT NULL,
  8. `platform` varchar(20) COLLATE utf8_polish_ci DEFAULT NULL,
  9. `keyword` varchar(60) COLLATE utf8_polish_ci DEFAULT NULL,
  10. `ip` int(10) UNSIGNED DEFAULT NULL,
  11. `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  12. PRIMARY KEY (`id`),
  13. KEY `page` (`page`,`back`,`domain`,`browser`, `platform`, `keyword`,`ip`),
  14. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci AUTO_INCREMENT=2621940 ;


Da się to jakoś przyśpieszyć?
nospor
Skoro grupujesz po domain to wypadałoby na to pole założyć index
sebekzosw
hmm, indeks jest nałożony
cv65
Pokaż co Ci mówi EXPLAIN
sebekzosw
Struktura:

  1. CREATE TABLE IF NOT EXISTS `all_cms_entry` (
  2. `id` mediumint(8) UNSIGNED NOT NULL AUTO_INCREMENT,
  3. `page` varchar(50) COLLATE utf8_polish_ci DEFAULT NULL,
  4. `back` enum('0','1') COLLATE utf8_polish_ci DEFAULT '0',
  5. `domain` varchar(70) COLLATE utf8_polish_ci DEFAULT NULL,
  6. `referer` varchar(100) COLLATE utf8_polish_ci DEFAULT NULL,
  7. `browser` varchar(30) COLLATE utf8_polish_ci DEFAULT NULL,
  8. `platform` varchar(20) COLLATE utf8_polish_ci DEFAULT NULL,
  9. `keyword` varchar(60) COLLATE utf8_polish_ci DEFAULT NULL,
  10. `ip` int(10) UNSIGNED DEFAULT NULL,
  11. `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  12. PRIMARY KEY (`id`),
  13. KEY `page` (`page`,`back`,`domain`,`browser`,`platform`,`keyword`,`ip`)
  14. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci AUTO_INCREMENT=3670017 ;


maly_swd
to:
KEY `page` (`page`,`back`,`domain`,`browser`, `platform`, `keyword`,`ip`),

Nie oznacza ze masz nalozony index na domain!

Takie cos oznacza ze masz poprawnie;)
KEY `domain` (`domain`),


sebekzosw
  1. SELECT `domain`, COUNT(`id`) AS `count` FROM `all_cms_entry` GROUP BY `domain` ORDER BY `count` DESC


  1. CREATE TABLE IF NOT EXISTS `all_cms_entry` (
  2. `id` mediumint(8) UNSIGNED NOT NULL AUTO_INCREMENT,
  3. `page` varchar(50) COLLATE utf8_polish_ci DEFAULT NULL,
  4. `back` enum('0','1') COLLATE utf8_polish_ci DEFAULT '0',
  5. `domain` varchar(70) COLLATE utf8_polish_ci DEFAULT NULL,
  6. `referer` varchar(100) COLLATE utf8_polish_ci DEFAULT NULL,
  7. `browser` varchar(30) COLLATE utf8_polish_ci DEFAULT NULL,
  8. `platform` varchar(20) COLLATE utf8_polish_ci DEFAULT NULL,
  9. `keyword` varchar(60) COLLATE utf8_polish_ci DEFAULT NULL,
  10. `ip` int(10) UNSIGNED DEFAULT NULL,
  11. `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  12. PRIMARY KEY (`id`),
  13. KEY `domain` (`domain`),
  14. KEY `browser` (`browser`),
  15. KEY `platform` (`platform`),
  16. KEY `keyword` (`keyword`),
  17. KEY `ip` (`ip`)
  18. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci AUTO_INCREMENT=2097153 ;


w skrypcie pokazuje czas ładowania 4sec, jednak w phpmyadmin ładuje, ładuje i ładuje



jest dobrze?
BaN
Jest źle, baza nie korzysta z indeksu, zamień COUNT(`id`) na COUNT(*) i zobacz EXPLAIN, powinna skorzystać z indeksu. Jeśli dalej nie, to wykonaj polecenie
  1. CHECK TABLE `all_cms_entry`
I jeszcze raz EXPLAIN. EXPLAIN powinien pokazać korzystanie z indeksu, dodatkowo pewnie dalej będzie tworzenie tabeli tymczasowej i filesort, bo masz sortowanie, ale powinno być szybkie jeśli założy tabelę w pamięci, a nie na dysku. Dużo jest tych unikalnych domen?
sebekzosw
"Using index; Using temporary; Using filesort" już jest ok w sumie

tych unikalnych domen w sumie to 3 są, jednak powieliłem to wszystko do testów smile.gif

Cytat
zamień COUNT(`id`) na COUNT(*)


zamieniłem i już czuć różnicę smile.gif

Cytat
ale powinno być szybkie jeśli założy tabelę w pamięci, a nie na dysku


da się ustawić, aby zakładało tabele w pamięci a nie na dysku? jeżeli tak to jak?
maly_swd
"da się ustawić, aby zakładało tabele w pamięci a nie na dysku? jeżeli tak to jak?"

Da sie, ale trzeba miec dostep do ustawien konfiguracyjnych mysql, sa tam odpowiednie zapisy dal tabel tymczasowych i wielkosci przydzielonego ramu.
mmmmmmm
ENGINE=MEMORY
http://dev.mysql.com/doc/refman/5.0/en/storage-engines.html
sebekzosw
a może mi ktoś pomoże jeszcze z jednym problemem. Chce zrobić licznik średniego czasu spędzonego na stronie:

  1. SELECT UNIX_TIMESTAMP(MAX(`date`))- UNIX_TIMESTAMP(MIN(`date`)) AS `sec`, `eid` FROM `all_cms_entry`
  2. WHERE `eid` > 0
  3. GROUP BY `eid`
  4. ORDER BY `all_cms_entry`.`eid` DESC


wynik to:

sec eid
7 2097172
2 2097171
9 2097170
4 2097169
14 2097168

a powinno być suma sekund / ilość rekordów

Średni czas to: 7.2


nie wiem jak to zsumować smile.gif
maly_swd
normalnie powino byc to tak:

  1. SELECT avg(UNIX_TIMESTAMP(MAX(`date`))- UNIX_TIMESTAMP(MIN(`date`)) ) AS `sec`, `eid` FROM `all_cms_entry`
  2. WHERE `eid` > 0
  3. GROUP BY `eid`
  4. ORDER BY `all_cms_entry`.`eid` DESC


pod warunkiem ze masz dobrze zrobiona baze:)

ale u Ciebie moze byc tez tak:

  1. SELECT avg(UNIX_TIMESTAMP(MAX(`date`))- UNIX_TIMESTAMP(MIN(`date`)) ) AS `sec`, `eid` FROM `all_cms_entry`
  2. WHERE `eid` > 0
  3. ORDER BY `all_cms_entry`.`eid` DESC


podaj co to jest to eid... i czemu po tym grupujesz?
sebekzosw
jeżeli ktoś wejdzie za pierwszym razem na stronę to do bazy leci wpis o wejściu i inne dane. wtedy do sesji zapisuje id pierwszego wejścia (eid). Jeżeli wejdzie drugi raz to również zapisuje dane do bazy + eid. Domyślnie to pole ma ustawiona wartość 0
maly_swd
i jak? dziala Ci ten przyklad z AVG() co podalem?
sebekzosw
przykład nie działa, błąd #1111 ;/
maly_swd
  1. SELECT avg(a.sec) FROM (SELECT UNIX_TIMESTAMP(MAX(`date`))- UNIX_TIMESTAMP(MIN(`date`)) AS `sec`, `eid` FROM `all_cms_entry`
  2. WHERE `eid` > 0
  3. GROUP BY `eid`
  4. ORDER BY `all_cms_entry`.`eid` DESC
  5. ) AS a


moim zdaniem cos masz namieszane w danych i zalozeniach, czemu liczycz to w taki sposob? UNIX_TIMESTAMP(MAX(`date`))- UNIX_TIMESTAMP(MIN(`date`)
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.