2016-07-21 10 views
6

Podnosimy poziom do mysql 5.7 i odkrywamy, że jest on znacznie wolniejszy niż jego licznik 5,6. Chociaż obie mają prawie identyczną konfigurację, wersja 5.6 wykonuje większość elementów sqls w milisekundach, podczas gdy druga zajmuje około 1 sekundy lub więcej dla średniego złożonego sql jak na przykład poniżej.mysql 5.7 jest znacznie wolniejszy niż mysql 5.6 w średnim sql

-- Getting most recent users that are email-verified and not banned 

SELECT 
    `u`.* 
FROM 
    `user` AS `u` 
INNER JOIN `user` user_table_alias ON user_table_alias.`id` = `u`.`id` 
LEFT JOIN `user_suspend` user_suspend_table_alias ON user_suspend_table_alias.`userId` = `user_table_alias`.`id` 
WHERE 
    (
     `user_suspend_table_alias`.`id` IS NULL 
    ) 
AND 
    `user_table_alias`.`emailVerify` = 1 

ORDER BY 
    `u`.`joinStamp` DESC 
LIMIT 1, 18 

Obie tabele są dość proste i dobrze indeksowane:

-- ---------------------------- 
-- Table structure for user 
-- ---------------------------- 
DROP TABLE IF EXISTS `user`; 
CREATE TABLE `user` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `email` varchar(128) NOT NULL DEFAULT '', 
    `username` varchar(32) NOT NULL DEFAULT '', 
    `password` varchar(64) NOT NULL DEFAULT '', 
    `joinStamp` int(11) NOT NULL DEFAULT '0', 
    `activityStamp` int(11) NOT NULL DEFAULT '0', 
    `accountType` varchar(32) NOT NULL DEFAULT '', 
    `emailVerify` tinyint(2) NOT NULL DEFAULT '0', 
    `joinIp` int(11) unsigned NOT NULL, 
    `locationId` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `email` (`email`), 
    UNIQUE KEY `username` (`username`), 
    KEY `accountType` (`accountType`), 
    KEY `joinStamp` (`joinStamp`), 
    KEY `activityStamp` (`activityStamp`) 
) ENGINE=MyISAM AUTO_INCREMENT=89747 DEFAULT CHARSET=utf8 COMMENT='utf8_general_ci'; 

-- ---------------------------- 
-- Table structure for user_suspend 
-- ---------------------------- 
DROP TABLE IF EXISTS `user_suspend`; 
CREATE TABLE `user_suspend` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `userId` int(11) DEFAULT NULL, 
    `timestamp` int(11) DEFAULT NULL, 
    `message` text NOT NULL, 
    `expire` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `userId` (`userId`) 
) ENGINE=MyISAM AUTO_INCREMENT=513 DEFAULT CHARSET=utf8; 

Tabele mają około 100K i 1K wiersze odpowiednio. Zauważyłem dwa interesujące zachowania, które chciałbym "naprawić":

  1. Usunięcie klauzuli ORDER BY powoduje, że czas wykonywania egzaminu wynosi od ~ 1,2 s do 0,0015 s!
  2. SQL nie są buforowane przez mysql 5.7

Uwaga: mamy zapytanie cache:

SHOW STATUS LIKE 'Qcache%'

Qcache_free_blocks 19408 
Qcache_free_memory 61782816 
Qcache_hits 31437169 
Qcache_inserts 2406719 
Qcache_lowmem_prunes 133483 
Qcache_not_cached 43555 
Qcache_queries_in_cache 41691 
Qcache_total_blocks 103951 

google i dowiedziałem się wiele problemów zgłosił się na 5.7, ale nie rozumiem, dlaczego te dziwne zachowania w tym sql (nadal wiele innych sqls, które działają znacznie wolniej na 5.7).

Oto EXPLAIN, sugerowane przez Neville K:

id select_type  table    partitions type  possible_keys key   key_len  ref rows filtered Extra 
1 SIMPLE  user_table_alias  NULL  ALL   PRIMARY  NULL  NULL  NULL 104801 10.00 Using where; Usingtemporary; Usingfilesort 
1 SIMPLE  u    NULL  eq_ref  PRIMARY  PRIMARY  4  knn.base_user_table_alias.id 1 100.00 NULL 
1 SIMPLE  user_suspend_table_alias NULL  ref   userId userId   5  knn.base_user_table_alias.id 1 10.00 Using where; 
+0

Więc ... jeśli chcesz prędkości, dlaczego używasz MyISAM? – Mjh

+1

Czy możesz napisać EXPLAIN? –

+0

Skorzystaj z "profilu", aby przeanalizować koszt czasowy w SQL; może pokazywać koszt czasowy w każdej fazie; –

Odpowiedz

2

W INNER JOIN user user_table_alias NA user_table_alias. id = u. id wygląda bezużytecznie. Łączy się tylko przeciwko sobie i ta technika nie jest używana w pozostałej części zapytania.

Brak indeksu na emailVerify. Który jest wskazany przez pierwszy wiersz EXPLAIN. ("używając gdzie" oznacza brak indeksu)

To zapytanie nie jest skalowane z rozmiarem tabeli, ponieważ pełna tabela musi być rozpatrzona przed wyznaczeniem "ostatnich użytkowników". Więc prawdopodobnie jakiś wewnętrzny bufor używany przez myisam jest teraz przepełniony. To właśnie oznacza "używanie tymczasowe". Używanie filesort oznacza, że ​​kolejność jest tak duża, że ​​używa pliku temp, który jest szkodliwy dla wydajności.

+0

faktycznie, zmieniłem 'user_table_alias''emailVerify' = 1 =>' u'.'emailVerify' = 1 i jest znowu szybki. Nie wiem dlaczego, ale w mysql.6, oba są szalenie szybkie –

0

Ok, dzięki NevilleK na Wyjaśnienie.

I zorientowali się, jak już tylko rozwiązać ten sql:

user_table_alias.emailVerify = 1 

do

u.emailVerify = 1 

Nie wiem dlaczego, ale w mysql5.6, oba są wykonywane w millisecs.

Chyba będę musiał przejrzeć wszystkie zapytania SQL (od innych deweloperów), dzięki Mysql wstecznej poprawy

1

Self-join wygląda zbędne.

myślę można ponownie napisać kwerendę w następujący sposób:

SELECT 
    `u`.* 
FROM 
    `user` AS `u` 
LEFT JOIN `user_suspend` user_suspend_table_alias ON user_suspend_table_alias.`userId` = `u`.`id` 
WHERE `user_suspend_table_alias`.`id` IS NULL 
AND  `u`.`emailVerify` = 1 
ORDER BY 
    `u`.`joinStamp` DESC 
LIMIT 1, 18 

zakładam „emailVerify” jest kolumna z zaledwie kilku wartości (0 i 1), a zatem powinno nie być indeksowane. Zakładam też, że "joinStamp" jest pewnego rodzaju znacznikiem czasu (chociaż typem danych jest liczba całkowita). Jeśli to prawda, możesz utworzyć indeks, aby przyspieszyć to dalej.

create index id_joinstamp on user (id, joinstamp) 
+0

Twoje zapytanie wygląda ładniej, ale nasze "łączenia się z tabelami użytkownika" jest tutaj kawałkiem kodu wielokrotnego użytku. Oznacza to, że wiele innych tabel, takich jak "produkt", "post", "transakcja" ... itd., Jest również dołączanych do "user-user_suspend". Kod pochodzi z innego zespołu, ale nie narzekam. Spróbuję zastosować twoją radę –

Powiązane problemy