Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: wiadomości - optymalizacja
Forum PHP.pl > Forum > Bazy danych > MySQL
nospor
Mam takie tabele

  1. CREATE TABLE `message` (
  2. `ID` int UNSIGNED NOT NULL AUTO_INCREMENT,
  3. `FK_MESSAGE` int UNSIGNED NOT NULL DEFAULT 0 comment ,
  4. `FK_LAST_MESSAGE` int UNSIGNED NOT NULL DEFAULT 0 comment 'Id ostatniej odpowiedzi. wskazuje na siebie gdy nie ma odpowiedzi',
  5. `MESSAGE` text NOT NULL,
  6. `FK_CUSER` int NOT NULL COMMENT 'Kto utworzyl',
  7. PRIMARY KEY (`ID`),
  8. KEY `FK_MESSAGE` (`FK_MESSAGE`),
  9. KEY `FK_CUSER` (`FK_CUSER`),
  10. KEY `FK_LAST_MESSAGE` (`FK_LAST_MESSAGE`)
  11. ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
  12.  
  13. CREATE TABLE `message_user` (
  14. `FK_USER` int UNSIGNED NOT NULL comment 'Id usera',
  15. `FK_MESSAGE` int UNSIGNED NOT NULL comment 'Id wiadomosci',
  16. PRIMARY KEY (`FK_USER`,`FK_MESSAGE`)
  17. ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
  18.  

Mamy tabelę wiadomości oraz tabelę wiążącą, która mówi, że tę wiadomość może zobaczyć taki a taki user.
Wiadomość może też widziec ten, kto ją utworzył (FK_CUSER)

No i mam zapytanie pobierające wiadomosci
  1. SELECT cm.ID, cm.MESSAGE,cm.IP,cm.CDATE,cm.COUNT_MESSAGES
  2. FROM message cm
  3. LEFT JOIN message_user cmu ON (cmu.FK_MESSAGE=cm.ID AND cmu.FK_USER=4)
  4. WHERE cm.FK_MESSAGE = 0 AND (cm.FK_CUSER = 4 OR cmu.FK_USER IS NOT NULL)
  5. ORDER BY cm.FK_LAST_MESSAGE DESC LIMIT 5

Czyli pobieram wiadomosci jakie może widziec użytkownik 4
W bazie mam 500tys rekordów. Wszystko smiga do czasu dodania OR cmu.FK_USER IS NOT NULL czyli sprawdzeniu tabeli wiążącej. EXPLAIN daje:
Kod
id    select_type    table    type    possible_keys    key    key_len    ref    rows    Extra
1    SIMPLE    cm    index    FK_MESSAGE,FK_CUSER    FK_LAST_MESSAGE    4    \N    35    Using where
1    SIMPLE    cmu    eq_ref    PRIMARY    PRIMARY    8    cm.ID,const    1    Using where; Using index

Gdy dodam tego OR to zapytanie wykonuje się 2 sekundy. Idzie jakoś to przyspieszyc? Nie widzę za bardzo jakie mam tu dodać dodatkowe indeksy.


Przed chwilą wpadłem na pomysł, że wystarczy iż zamienie
cm.FK_CUSER = 4 OR cmu.FK_USER IS NOT NULL
na
cmu.FK_USER IS NOT NULL

czyli wywale sprawdzanie autora wiadomosci, a autora dodam do tabeli wiążącej. Wówczas zapytanie znowu śmiga. Wolałbym jednak tego rozwiązania nie stosować.
BaN
Nie za bardzo rozumiem

1. W zapytaniu używasz pól cm.CDATE, cm.COUNT_MESSAGES, a w definicji tabeli `message` brak takich pól. Zapytanie raczej nie powinno działać smile.gif

2. Po co jest tabela `message_user`? Dane dotyczące identyfikatorów wiadomości i użytkownika są już w tabeli `message`
nospor
ad1) Hehe, wyciałem parę nic nie znaczących pol z tabeli. zapomniałem ich wyciąć w SELECT - potraktuj ze ich tam nie ma smile.gif
ad2) message_user zawiera informacje do których użytkowników kierowana jest wiadomosc. W tabeli message nie ma takich informacji.
BaN
Cytat(nospor @ 25.10.2010, 19:03:35 ) *
ad2) message_user zawiera informacje do których użytkowników kierowana jest wiadomosc. W tabeli message nie ma takich informacji.

Jak rozumiem przy zakładaniu wiadomości użytkownik wybiera do jakich użytkowników jest kierowana wiadomość, nie dyskutuję, bo to funkcjonalność, widocznie tak ma być

Mam dwie propozycje i jedną poprawkę:
  1. założyć indeks podwójny lub nawet potrójny w tabeli `message`, nie wiem jakie masz pozostałe zapytania, więc trudno określić jakie indeksy będą najbardziej odpowiednie, ale minimalny indeks przynajmniej na polach (FK_MESSAGE, FK_LAST_MESSAGE), sam indeks na (FK_LAST_MESSAGE) chyba nie jest w ogóle potrzebny
  2. zmienić odrobinę zapytanie, w zasadzie to poprawka kosmetyczna, nie powinno to przyśpieszyć zapytania, ale jest logiczne
    zamiast:
    1. WHERE cm.FK_MESSAGE = 0 AND (cm.FK_CUSER = 4 OR cmu.FK_USER IS NOT NULL)
    wpisać:
    1. WHERE cm.FK_MESSAGE = 0 AND (cm.FK_CUSER = 4 OR cmu.FK_USER=4)
    gdyż z wyniku złączenia uzyskasz albo cmu.FK_USER równe NULL albo 4
  3. zmienić zapytanie nie na łączenie tabel, tylko skorzystać z warunku EXISTS:
    1. SELECT ...
    2. FROM message cm
    3. WHERE cm.FK_MESSAGE = 0
    4. AND (cm.FK_CUSER = 4
    5. OR EXISTS (SELECT 1 FROM message_user cmu WHERE cmu.FK_MESSAGE=cm.ID AND cmu.FK_USER=4 LIMIT 1))
    6. ORDER BY cm.FK_LAST_MESSAGE DESC LIMIT 5
nospor
Cytat
Jak rozumiem przy zakładaniu wiadomości użytkownik wybiera do jakich użytkowników jest kierowana wiadomość, nie dyskutuję, bo to funkcjonalność, widocznie tak ma być
Tak, tak ma byc smile.gif

ad1) Bawiłem się już i taką kombinacją - nic to nie dało
ad2) Zrobiłem to celowo. Dzięki mojemu zastosowaniu unikam duplikacji głównego rekordu, przy kilku wpisach w tabeli wiążącej dla danej wiadomości
ad3) No niestety nic to nie dało. Mało tego, to rozwiązanie jest jeszcze gorsze, gdy zmula zapytanie nawet wówczas gdy wawalę z OR ten pierwszy warunek na FK_CUSER. Gdy zostawię joina zamiast EXISTS, to po wywaleniu FK_CUSER z OR zapytanie śmiga jak ta lala.

Kombinuj dalej. Chętnie wysłucham jeszcze jakiś propozycji. Szczerze powiedziawszy gdy pokazałeś EXISTS to już miałem cień nadziei że zadziała.
BaN
Cytat(nospor @ 25.10.2010, 22:30:24 ) *
ad1) Bawiłem się już i taką kombinacją - nic to nie dało

Dziwne, baza powinna skorzystać z podwójnego indeksu. Jakaś stara wersja bazy? To w takim razie wymuś skorzystanie z indeksu za pomocą FORCE INDEX. Jaki jest EXPLAIN?

Cytat(nospor @ 25.10.2010, 22:30:24 ) *
Kombinuj dalej. Chętnie wysłucham jeszcze jakiś propozycji. Szczerze powiedziawszy gdy pokazałeś EXISTS to już miałem cień nadziei że zadziała.


Nie mam danych i tabel, a nie chce mi się wpisywać smile.gif Kolejna propozycja: użyj UNION
nospor
FORCE INDEX rzeczywiście przyspieszyło zapytanie. Aczkolwiek dla pewnych warunków danych. Trzeba by się pobawić i dobrać optymalne. Nie mniej jednak FORCE INDEX nie rozwiązał całkowicie problemu, gdyż nadal za wolno dane się szukały.

Na UNION wpadłem przed Twoim postem. Potestowałem i dalo zadowalające wyniki. Chyba więc zostanę przy UNION, szczególnie gdyż będę miał jeszcze kilka złączeń i warunków oprócz tych co tu podałem.
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.