2011-09-29 9 views
10

mam następującą tabelę MySQL (uproszczony):Dlaczego usunięcie tego indeksu w MySQL przyspieszy moje zapytanie o 100x?

CREATE TABLE `track` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `title` varchar(256) NOT NULL, 
    `is_active` tinyint(1) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `is_active` (`is_active`, `id`) 
) ENGINE=MyISAM AUTO_INCREMENT=7495088 DEFAULT CHARSET=utf8 

do „is_active” kolumna wyznacza wiersze, które chcę ignorować w większości, ale nie wszystkich, moich zapytaniami. Mam kilka zapytań, które okresowo odczytują porcje z tej tabeli. Jeden z nich wygląda następująco:

SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10; 

Wykonanie tej kwerendy trwa ponad minutę. Oto plan wykonanie:

> EXPLAIN SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10; 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 
| 1 | SIMPLE  | t  | ref | PRIMARY,is_active | is_active | 1  | const | 3747543 | Using where | 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 

Teraz, jeśli powiem MySQL ignorować „” is_active indeksu, zapytanie dzieje się natychmiast.

> EXPLAIN SELECT id,title from track IGNORE INDEX(is_active) WHERE (track.is_active=1 AND track.id > 5580702) ORDER BY id ASC LIMIT 10; 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | t  | range | PRIMARY  | PRIMARY | 4  | NULL | 1597518 | Using where | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 

Teraz, co jest naprawdę dziwne jest to, że jeśli wymusić MySQL użyć „” indeks is_active, kwerenda po raz kolejny stanie się natychmiast!

+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | t  | range | is_active  |is_active| 5  | NULL | 1866730 | Using where | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 

Po prostu nie rozumiem tego zachowania. W indeksie "is_active" wiersze należy sortować według is_active, a następnie id. W moim zapytaniu używam zarówno kolumn "is_active", jak i "id", więc wydaje się, że trzeba tylko wykonać kilka przeskoków wokół drzewa, aby znaleźć identyfikatory, a następnie użyć tych identyfikatorów do pobrania tytułów z tabeli.

Co się dzieje?

EDIT: Więcej informacji na temat tego, co robię:

  • cache zapytań jest wyłączone
  • Bieg OPTYMALIZACJA TABELA i analizować TABELA nie miał wpływu
  • 6.620.372 wierszy 'is_active' ustawioną na True. 874,714 wierszy ma "is_active" ustawione na False.
  • Użycie FORCE INDEX (is_active) jeszcze raz przyspiesza zapytanie.
  • wersja MySQL 5.1.54
+2

Czyścisz pamięć podręczną przed testowaniem porównawczym, prawda? – dfb

+0

Upewnij się także, że statystyki tabeli są aktualne, a indeksy są odbudowywane. (Jednak robi się to w MySQL ;-) –

+0

Co stanie się, jeśli odwrócisz warunki WHERE? 'gdzie (track.id> 5580702 i track.is_active = 1)' – EJP

Odpowiedz

7

Wygląda na to, że MySQL podejmuje niewłaściwą decyzję o korzystaniu z indeksu.

Z tego planu zapytań wynika, że ​​mógł użyć indeksu PRIMARY lub is_active, i wybrał is_active, aby najpierw zawęzić według track.is_active. Jednak używa tylko pierwszej kolumny indeksu (track.is_active). Otrzymuje to 3747543 wyniki, które następnie muszą zostać przefiltrowane i posortowane.

Jeśli wybrał indeks PRIMARY, byłby w stanie zawęzić do 1597518 wierszy za pomocą indeksu i zostałyby one odzyskane w kolejności track.id już, co nie powinno wymagać dalszego sortowania. To byłoby szybsze.

Nowe informacje:

W trzecim przypadku, gdy używasz FORCE INDEX, MySQL używa indeksu is_active ale teraz zamiast tylko za pierwszą kolumnę, to jest przy użyciu obu kolumnach (patrz key_len). Teraz jest teraz w stanie zawęzić przez is_active i sortować i filtrować według id, używając tego samego indeksu, a ponieważ is_active jest pojedynczą stałą, ORDER BY jest spełniony przez drugą kolumnę (tj. Wiersze z pojedynczej gałęzi indeksu są już w posortowanej kolejności). Wydaje się, że jest to jeszcze lepszy wynik niż użycie PRIMARY - i prawdopodobnie to, co zamierzałeś, prawda?

Nie wiem, dlaczego nie używał obu kolumn tego indeksu bez wskaźnika FORCE, chyba że zapytanie zmieniło się w subtelny sposób pomiędzy. Jeśli nie, odłożyłbym to do MySQL, podejmując złe decyzje.

+0

Oczywiście, jeśli wiesz lepiej niż MySQL, zawsze możesz użyć [USE INDEX()] (http://dev.mysql.com/doc/refman/5.5/en/index-hints.html), aby zasugerować, który indeks to powinien preferować. Możesz także wypróbować [ANALYZE TABLE] (http://dev.mysql.com/doc/refman/5.5/en/analyze-table.html), aby dać MySQL szansę samodzielnego samodzielnego rozwiązania, co może czasem zadziałać. . – thomasrutter

+0

Jeśli użyję FORCE INDEX (is_active), zapytanie pojawi się natychmiast (zobacz ostatnie zmiany). Jakieś pomysły? – cwick

+0

Nie jestem pewien - może jakaś skrzynka? Może dodać do tego wyjście EXPLAIN? Czy otrzymujesz te same wyniki w tej samej kolejności? – thomasrutter

1

myślę, że przyspieszenie jest ze względu na swój klauzuli WHERE. Zakładam, że pobiera on tylko niewielki podzbiór wierszy w całej dużej tabeli. Szybsze jest skanowanie tabeli pobranych danych dla is_active na małym podzbiorze niż filtrowanie przez duży plik indeksu. Przemierzanie indeksu pojedynczej kolumny jest znacznie szybsze niż przemierzanie połączonego indeksu.

Powiązane problemy