2017-09-05 19 views
5

Pracuję nad aplikacją śledzenia sieci społecznościowej. Nawet łączenie działa poprawnie z odpowiednim indeksowaniem. Ale gdy dodaję klauzulę order by, zapytanie całkowite trwa 100 razy dłużej. Poniższe zapytanie użyłem do pobrania twitter_users bez klauzuli order by.Jak poprawić porządek dzięki wydajności z połączeniami w mysql

SELECT DISTINCT `tracked_twitter`.id 
FROM tracked_twitter 
INNER JOIN `twitter_content` ON `tracked_twitter`.`id` = `twitter_content`.`tracked_twitter_id` 
INNER JOIN `tracker_twitter_content` ON `twitter_content`.`id` = `tracker_twitter_content`.`twitter_content_id` 
AND `tracker_twitter_content`.`tracker_id` = '88' 
LIMIT 20 

rzędy Wyświetlono 0 - 19 (20 łącznie Zapytanie wziął 0,0714 sek)

Ale kiedy dodać ORDER BY (na indeksowanej kolumnie)

SELECT DISTINCT `tracked_twitter`.id 
FROM tracked_twitter 
INNER JOIN `twitter_content` ON `tracked_twitter`.`id` = `twitter_content`.`tracked_twitter_id` 
INNER JOIN `tracker_twitter_content` ON `twitter_content`.`id` = `tracker_twitter_content`.`twitter_content_id` 
AND `tracker_twitter_content`.`tracker_id` = '88' 
ORDER BY tracked_twitter.followers_count DESC 
LIMIT 20 

wiersze Wyświetlanie 0 - 19 (20 całkowita Query trwało 13.4636 s)

EXPLAIN enter image description here

Podczas realizacji zamówienia przez klauzuli tylko swojej tabeli nie zajmuje dużo czasu

SELECT * FROM `tracked_twitter` WHERE 1 order by `followers_count` desc limit 20 

rzędy Pokazuje 0 - 19 (20 Całkowita Query miało 0.0711 s) [followers_count: 68236387 - 10525612]

zapytanie tworzenie tabeli w następujący sposób

CREATE TABLE IF NOT EXISTS `tracked_twitter` (
    `id` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `handle` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `location` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `description` text COLLATE utf8_unicode_ci, 
    `profile_image` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `followers_count` int(11) NOT NULL, 
    `is_influencer` tinyint(1) NOT NULL DEFAULT '0', 
    `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
    `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
    `gender` enum('Male','Female','Other') COLLATE utf8_unicode_ci 
    DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `followers_count` (`followers_count`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

Więc dołącz nie spowolnić zapytanie i porządku przez pracę dobrze, kiedy mogę wykonać go na swojej tablicy. Jak mogę poprawić wydajność?

UPDATE 1

metoda @GordonLinoff rozwiązuje jeśli muszę tylko zestaw wyników z tabeli nadrzędnej. Co f Chciałbym poznać liczbę tweetów na osobę (liczba twitter_content pasujących do tabeli tracked_twitter). Jak mogę to zmienić? A jeśli chcę mieć funkcje matematyczne na tweetowej zawartości, jak mam to zrobić?

SELECT `tracked_twitter` . * , COUNT(*) AS twitterContentCount, retweet_count + favourite_count + reply_count AS engagement 
FROM `tracked_twitter` 
INNER JOIN `twitter_content` ON `tracked_twitter`.`id` = `twitter_content`.`tracked_twitter_id` 
INNER JOIN `tracker_twitter_content` ON `twitter_content`.`id` = `tracker_twitter_content`.`twitter_content_id` 
WHERE `is_influencer` != '1' 
AND `tracker_twitter_content`.`tracker_id` = '88' 
AND `tracked_twitter_id` != '0' 
GROUP BY `tracked_twitter`.`id` 
ORDER BY twitterContentCount DESC 
LIMIT 20 
OFFSET 0 
+0

Jak długo trwa nieuporządkowane zapytanie w przypadku usunięcia klauzuli "LIMIT"? "LIMIT" w nieuporządkowanym zestawie wyników w zasadzie oznacza "uzyskaj mi 20 rekordów, które pasują do moich kryteriów", podczas gdy w zamówionym wyniku oznacza "uzyskaj mi 20 pierwszych rekordów, które pasują do moich kryteriów", co w zasadzie oznacza, że ​​musisz określić wszystkie z nich. –

+0

Co zawiera dane wyjściowe polecenia "EXPLAIN" w zapytaniu? – raina77ow

+0

@ raina77ow Dodałem obrazek wyjaśniający w pytaniu – Tamizharasan

Odpowiedz

3

Spróbuj pozbyć się distinct. To jest zabójca wydajności. Nie jestem pewien, dlaczego twoje pierwsze zapytanie działa szybko; być może MySQL jest wystarczająco inteligentny, aby go zoptymalizować.

chciałbym spróbować:

SELECT tt.id 
FROM tracked_twitter tt 
WHERE EXISTS (SELECT 1 
       FROM twitter_content tc INNER JOIN 
        tracker_twitter_content ttc 
        ON tc.id = ttc.twitter_content_id 
       WHERE ttc.tracker_id = 88 AND 
        tt.id = tc.tracked_twitter_id 
      ) 
ORDER BY tt.followers_count DESC ; 

Dla tej wersji, chcesz indeksów: tracked_twitter(followers_count, id), twitter_content(tracked_twitter_id, id) i tracker_twitter_content(twitter_content_id, tracker_id).

+0

Działa doskonale, a kolejność według klauzuli nie spowolniła wykonywania kwerendy (łącznie 20, Query wziął 0,0707 s). Będę zadowolony, jeśli wyjaśnisz zapytanie. Tak, że zdobędę wiedzę na temat tego podejścia. A jeśli masz jakiś odnośnik do przeczytania o tych podejściach, będzie przydatny. Dzięki za odpowiedź. – Tamizharasan

+0

Przypuszczam, że pierwsze zapytanie działa szybko, ponieważ MySQL wie, że musi zebrać 20 różnych rekordów z nieuporządkowanego zestawu, który prawdopodobnie pochodzi z małej liczby wierszy (powiedzmy więcej niż 20, ale mniej niż tysiąc). Pierwsze 20 różnych od uporządkowanego zbioru milionów zgłoszeń będzie dużo wolniejsze - odrębne to albo trochę czerwonego śledzia, a to jest kolejność, która zabiera trochę czasu, albo może MySQL jest na tyle głupi, żeby nie tylko zamówić, ale następnie różne miliony wierszy przed powrotem tylko 20 –

+0

@CaiusJard. . . Właściwie, myślę, że MySQL może użyć indeksu na 'id', aby zapobiec duplikatom - po prostu czyta indeks w kolejności. Z wyraźnym "ORDER BY" nie jest to możliwe. –

1

tabela nadrzędna zachować na wsporniku z limitem

SELECT DISTINCT `tracked_twitter`.id FROM 
(SELECT id,followers_count FROM tracked_twitter ORDER BY followers_count DESC 
LIMIT 20) AS tracked_twitter 
INNER JOIN `twitter_content` ON `tracked_twitter`.`id` = `twitter_content`.`tracked_twitter_id` 
INNER JOIN `tracker_twitter_content` ON `twitter_content`.`id` = `tracker_twitter_content`.`twitter_content_id` 
AND `tracker_twitter_content`.`tracker_id` = '88' 
ORDER BY tracked_twitter.followers_count DESC 
+0

To nie może być odpowiedź. ponieważ nie uwzględniłeś innych ograniczeń tabel w klauzuli order by. Tak więc zajmuje tylko 20 rekordów z tabeli nadrzędnej i łączy się z innymi. Dostałem tylko dwa wiersze, gdy wykonuję zapytanie. – Tamizharasan

1

Głównym problemem jest to, że nawet, że masz stosunkowo niewiele wierszy, należy użyć varchar(255) COLLATE utf8_unicode_ci jako klucz podstawowy (zamiast liczb całkowitych), a tym samym jako klucza obcego w inne tabele. Ten sam problem, podejrzewam, jest z twitter_content.id. Powoduje to długie porównywanie ciągów i rezerwowanie dużej ilości dodatkowej pamięci dla tabel tymczasowych.

Dotyczy samej kwerendy, tak, powinna to być kwerenda, która przechodzi wzdłuż indeksu followers_count i sprawdza warunek powiązanych tabel. Można to zrobić tak, jak zasugerował Gordon Linoff, lub używając wskazówek indeksu.

Powiązane problemy