2014-09-05 4 views
5

Zapytanie:Optymalizacja długo kwerenda w mysql w ogromną wielkość stołu 33m wierszy

SELECT users.id as uid, name, avatar, avatar_date, driver, messages.id AS mid,messages.msg, messages.removed, messages.from_anonym_id, messages.t 
    o_anonym_id, (messages.date DIV 1000) AS date, from_id = 162077 as outbox, !(0 in (SELECT read_state FROM messages as msgs 
WHERE (msgs.from_id = messages.from_id or msgs.from_id = messages.user_id) and msgs.user_id = 162077 and removed = 0)) as read_state 
FROM dialog, messages, users 
WHERE messages.id = dialog.mid and ((uid1 = 162077 and users.id = uid2) or (uid2 = 162077 and users.id = uid1)) 
ORDER BY dialog.mid DESC LIMIT 0, 101; 

Stoły struktura:

mysql> desc messages; 
+----------------+------------------+------+-----+---------+----------------+ 
| Field   | Type    | Null | Key | Default | Extra   | 
+----------------+------------------+------+-----+---------+----------------+ 
| id    | int(11)   | NO | PRI | NULL | auto_increment | 
| from_id  | int(11)   | NO | MUL | NULL |    | 
| user_id  | int(11)   | NO | MUL | NULL |    | 
| group_id  | int(11)   | NO |  | NULL |    | 
| to_number  | varchar(30)  | NO | MUL | NULL |    | 
| msg   | text    | NO |  | NULL |    | 
| image   | varchar(20)  | NO |  | NULL |    | 
| date   | bigint(20)  | NO |  | NULL |    | 
| read_state  | tinyint(1)  | NO |  | 0  |    | 
| removed  | tinyint(1)  | NO | MUL | NULL |    | 
| from_anonym_id | int(10) unsigned | NO | MUL | NULL |    | 
| to_anonym_id | int(10) unsigned | NO | MUL | NULL |    | 
+----------------+------------------+------+-----+---------+----------------+ 

mysql> desc dialog; 
+----------------+------------------+------+-----+---------+----------------+ 
| Field   | Type    | Null | Key | Default | Extra   | 
+----------------+------------------+------+-----+---------+----------------+ 
| id    | int(11)   | NO | PRI | NULL | auto_increment | 
| uid1   | int(11)   | NO | MUL | NULL |    | 
| uid2   | int(11)   | NO | MUL | NULL |    | 
| mid   | int(11)   | NO | MUL | NULL |    | 
| from_anonym_id | int(10) unsigned | NO | MUL | NULL |    | 
| to_anonym_id | int(10) unsigned | NO | MUL | NULL |    | 
+----------------+------------------+------+-----+---------+----------------+ 


mysql> show index from messages; 
+----------+------------+----------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| Table | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | 
+----------+------------+----------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| messages |   0 | PRIMARY  |   1 | id    | A   | 42944290 |  NULL | NULL |  | BTREE  |   |    | 
| messages |   1 | user_id_2  |   1 | user_id  | A   |  2147214 |  NULL | NULL |  | BTREE  |   |    | 
| messages |   1 | user_id_2  |   2 | read_state  | A   |  2862952 |  NULL | NULL |  | BTREE  |   |    | 
| messages |   1 | user_id_2  |   3 | removed  | A   |  2862952 |  NULL | NULL |  | BTREE  |   |    | 
| messages |   1 | from_id  |   1 | from_id  | A   |  825851 |  NULL | NULL |  | BTREE  |   |    | 
| messages |   1 | from_id  |   2 | to_number  | A   |  825851 |  NULL | NULL |  | BTREE  |   |    | 
| messages |   1 | to_number  |   1 | to_number  | A   |   29 |  NULL | NULL |  | BTREE  |   |    | 
| messages |   1 | idx_user_id |   1 | user_id  | A   |  2044966 |  NULL | NULL |  | BTREE  |   |    | 
| messages |   1 | idx_from_id |   1 | from_id  | A   |  447336 |  NULL | NULL |  | BTREE  |   |    | 
| messages |   1 | removed  |   1 | removed  | A   |   29 |  NULL | NULL |  | BTREE  |   |    | 
| messages |   1 | from_anonym_id |   1 | from_anonym_id | A   |   29 |  NULL | NULL |  | BTREE  |   |    | 
| messages |   1 | to_anonym_id |   1 | to_anonym_id | A   |   29 |  NULL | NULL |  | BTREE  |   |    | 
+----------+------------+----------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
12 rows in set (0.01 sec) 

mysql> show index from dialog; 
+--------+------------+----------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| Table | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | 
+--------+------------+----------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| dialog |   0 | PRIMARY  |   1 | id    | A   |  6378161 |  NULL | NULL |  | BTREE  |   |    | 
| dialog |   1 | uid1   |   1 | uid1   | A   |  455582 |  NULL | NULL |  | BTREE  |   |    | 
| dialog |   1 | uid1   |   2 | uid2   | A   |  6378161 |  NULL | NULL |  | BTREE  |   |    | 
| dialog |   1 | uid2   |   1 | uid2   | A   |  2126053 |  NULL | NULL |  | BTREE  |   |    | 
| dialog |   1 | idx_mid  |   1 | mid   | A   |  6378161 |  NULL | NULL |  | BTREE  |   |    | 
| dialog |   1 | from_anonym_id |   1 | from_anonym_id | A   |   17 |  NULL | NULL |  | BTREE  |   |    | 
| dialog |   1 | to_anonym_id |   1 | to_anonym_id | A   |   17 |  NULL | NULL |  | BTREE  |   |    | 
+--------+------------+----------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 

PS proszę nie doradza mi żadnego przepisu tylko teoretyczne, praktyczne przykłady. Thx z góry.

Jeśli usunąć to stwierdzenie

!(0 in (SELECT read_state FROM messages as msgs 
WHERE (msgs.from_id = messages.from_id or msgs.from_id = messages.user_id) and msgs.user_id = 162077 and removed = 0)) as read_state 

kwerenda działa bardzo dobrze w porównaniu do oryginału: 101 wierszy w zestawie (0,04 sek)

przypuszczam to jest główny problem, ale muszę to tam pole. Może być ktoś, kto może zmienić tę rundę i uczynić ją szybszą, byłby bardzo zadowolony.

+0

Jak szybko działa samo zapytanie, bez usuniętej instrukcji? – kevin628

+0

Może się różnić w zależności od współczynnika obciążenia, ale teraz '101 rzędów w zestawie (5,59 s)' Jeśli system jest spokojny - 2-3 sekundy, co z pewnością stanowi problem. –

+1

Jeśli tabela 'messages' zawiera tabelę zawierającą 33 miliony rekordów, to zapytanie wewnętrzne - to, które usunięto w celu poprawy wydajności - wykonuje skanowanie tabeli 33 milionów rekordów dla każdego elementu w zapytaniu zewnętrznym (które jest ograniczona do 102 rekordów). Tak więc 102 * 33 miliony to DUŻO rekordów do przeskanowania. Rozważ użycie indeksów klucza złożonego, jeśli to możliwe. Jeśli nie, możesz rozważyć ponowne przeanalizowanie sposobu powiązania danych, aby móc korzystać z indeksów kluczy złożonych. – kevin628

Odpowiedz

3

Zacznę od indeksu w tabeli komunikatów. Indeks złożony, który pomoże pokryć złączenie, tak jak w przykładowym zapytaniu poniżej ... Indeks na (identyfikator_użytkownika, usunięty, stan_zdaniowy, od_udziału).

Następnie wyjaśnienie mojego procesu. Robię wstępną kwerendę z tabeli okien dialogowych jako UNION, ale każdego z osobna, chwytając przeciwny identyfikator dla "LinkToUser" dla następnego cyklu łączącego się z tabelą użytkownika raz kontra wynik połączenia "OR", tak jak w klauzuli where. Zdobycie kwalifikowanych rekordów z góry i uproszczonych może ci pomóc.

Następna część to miejsce, w którym pojawi się indeks wiadomości. Robię lewe połączenie w oparciu o konkretnego użytkownika, usunięto = 0 i SZCZEGÓŁOWO read_state = 0. Używając indeksu, albo znajdzie pasujące, albo nie. Tak więc klauzula pola Wybrane pole (! 0 w ...) jest po prostu uproszczona do sprawdzenia IS NULL.

SELECT 
     u.id as uid, 
     u.name, 
     avatar, 
     avatar_date, 
     driver, 
     m.id AS mid, 
     m.msg, 
     m.removed, 
     m.from_anonym_id, 
     m.to_anonym_id, 
     (m.date DIV 1000) AS date, 
     from_id = 162077 as outbox, 
     msgFrom.from_id IS NULL as read_state 
    FROM 
     (select distinct d1.*, d1.uid2 as LinkToUser 
      from dialog d1 
      where d1.uid1 = 162077 
     union select d2.*, d2.uid1 as LinkToUser 
      from dialog d2 
      where d2.uid2 = 162077) Qualified 

     JOIN Users u 
      ON Qualified.LinkToUser = u.id 

     JOIN Messages m 
      ON Qualified.mid = m.id 

      LEFT JOIN Messages msgFrom 
       ON msgFrom.user_id = 160277 
       AND msgFrom.Removed = 0 
       AND msgFrom.Read_State = 0 
       AND ( m.from_id = msgFrom.from_id 
        OR m.user_id = msgFrom.from_id) 

    ORDER BY 
     Qualified.mid DESC 
    LIMIT 
     0, 101; 

może trzeba grać z nim trochę, może zmienić na coś takiego ..

if(msgFrom.from_id IS NULL, 0, msgFrom.read_state) as Read_State 

WYJAŚNIENIE

Zeusakm Twój indywidualny pola dla read_state jako pisemny będzie zwrócić tylko 1 lub 0, ponieważ jest to logiczny warunek NIE wartości zero na wybranej liście wiadomości. Nigdy nie zwróci wartości -1, jak wskazano w komentarzu. Moja wersja robi to samo. Jeśli znajdzie zero, zwróci zero. Jeśli nie może znaleźć zera, zwraca 1, ponieważ wartość porównania będzie równa NULL, a zatem "IsThisValue IS NULL" zwróci wartość true, która jest taka sama jak flaga 1.

Mam nadzieję, że to wyjaśnia, co robiłem z lewostronnym dla ciebie. Jawnie poszukaj ID użytkownika, usunięty stan i stan odczytu oraz dopasowanie (z lub użytkownika).

+0

+1 za spróbuj, ale przykro mi - małe, masywne rozwiązanie, np .: Mogę po prostu wyciągnąć blachę kotła i wybrać je na wynik, a następnie zdefiniować według wartości w tablicy, czy jest jakiś element z 1 wartością - będzie więcej wdzięczny. Dzięki. –

+0

Nie, główny problem nie został rozwiązany - '! (0 in (WYBIERZ stan_obsługiwany z wiadomości jako msgs.s.s.jWHERE (msgs.from_id = messages.from_id lub msgs.from_id = messages.user_id) i msgs.user_id = 162077 i usunięto = 0)) jako read_state', choć thx for try. –

+0

@zeusakm, nie widząc danych i naprawdę rozumiejąc Twój! (0 w ...), nie mogę zrobić dużo więcej. – DRapp

3

To jest zapytanie z join składnia stałe i aliasy tabeli dodany do tabel w zapytaniu zewnętrznym:

SELECT u.id as uid, name, avatar, avatar_date, driver, m.id AS mid, m.msg, 
     m.removed, m.from_anonym_id, m.t 
     o_anonym_id, (m.date DIV 1000) AS date, from_id = 162077 as outbox, 
     !(0 in (SELECT read_state 
       FROM messages m2 
       WHERE (m2.from_id = m.from_id or m2.from_id = m.user_id) and 
        m2.user_id = 162077 and removed = 0 
      ) 
     ) as read_state 
FROM dialog d join 
    messages m 
    on m.id = d.mid join 
    users u 
    on (uid1 = 162077 and users.id = uid2) or 
     (uid2 = 162077 and users.id = uid1) 
ORDER BY d.mid DESC 
LIMIT 0, 101; 

Jeśli zapytanie działa dobrze bez podzapytania w klauzuli select, polecam zastąpienie że . in może być drogim operatorem, w szczególności z or na warunkach.Dlatego polecam zastąpienie go:

(case when exists (select 1 
        from messages m2 
        where m2.user_id = 162077 and m2.removed = 0 and 
          m2.from_id = m.from_id and m2.read_state = 0 
        ) 
     then 0 
     when exists (select 1 
        from messages m2 
        where m2.user_id = 162077 and m2.removed = 0 and 
          m2.from_id = m.user_id and m2.read_state = 0 
        ) 
     then 0 
     else 1 
    end) 

I chcesz indeks na messages(from_id, user_id, removed, read_state).

+0

Niestety, górne zapytanie z Twojego postu trwa 4,5 s, z dolną poprawką (zapytanie zagnieżdżone) 5,6 sek. Wydaje się być gorszym niż oryginalnym, przepraszam. Thx dla try. –

+1

@zeusakm. . . Czy masz odpowiedni indeks na 'wiadomościach'? –

+0

Zdecydowanie, ustawiam indeks 'messages (from_id, user_id, removed, read_state)' przed uruchomieniem zapytania. –

2

utwórz tabelę tymczasową i wstaw wszystkie kolumny oprócz parametru readstate z wartością domyślną równą -1, a także zapisz numer_formularza zaktualizuj kolumnę odczytu, podobną do posta Gordona.

CREATE TEMPORARY TABLE userTable 
SELECT u.id as uid, name, avatar, avatar_date, driver, m.id AS mid, m.msg, 
     m.removed, m.from_anonym_id, m.t 
     o_anonym_id, (m.date DIV 1000) AS date, from_id = 162077 as outbox, 
     m.form_id, 
     -1 as read_state 
FROM dialog d join 
    messages m 
    on m.id = d.mid join 
    users u 
    on (uid1 = 162077 and users.id = uid2) or 
     (uid2 = 162077 and users.id = uid1) 
ORDER BY d.mid DESC 
LIMIT 0, 101; 

update userTable set readstate = 
(case when exists (select 1 
        from messages m2 
        where m2.user_id = 162077 and m2.removed = 0 and 
          m2.from_id = userTable.from_id and m2.read_state = 0 
        ) 
     then 0 
     when exists (select 1 
        from messages m2 
        where m2.user_id = 162077 and m2.removed = 0 and 
          m2.from_id = userTable.uid and m2.read_state = 0 
        ) 
     then 0 
     else 1 
    end) 
Powiązane problemy