2013-06-13 12 views
8

Mam tabelę Foo z 200 milionami rekordów i tablicą z 1000 rekordami, są one połączone wiele do jednego. Istnieją indeksy dla kolumn Foo.someTime i Bar.someField. Również w barze 900 zapisy someField 1, 100 mają someField z 2.Używanie indeksu na wewnętrznym sprzężeniu tabeli w MySQL

(1) To zapytanie wykonuje natychmiast:

mysql> select * from Foo f inner join Bar b on f.table_id = b.table_id where f.someTime  between '2008-08-14' and '2018-08-14' and b.someField = 1 limit 20; 
... 
20 rows in set (0.00 sec) 

(2) Ten właśnie trwa na wieki (jedyna zmiana jest b. someField = 2):

mysql> select * from Foo f inner join Bar b on f.table_id = b.table_id where f.someTime  between '2008-08-14' and '2018-08-14' and b.someField = 2 limit 20; 

(3) Ale jeśli wypadają gdzie klauzula niż kiedyś też wykonuje natychmiast:

mysql> select * from Foo f inner join Bar b on f.table_id = b.table_id where b.someField = 2 limit 20; 
... 
20 rows in set (0.00 sec) 

(4) Również mogę ją przyspieszyć przez wymuszenie użycia indeksu:

mysql> select * from Foo f inner join Bar b force index(someField) on f.table_id = b.table_id where f.someTime  between '2008-08-14' and '2018-08-14' and b.someField = 2 limit 20; 
... 
20 rows in set (0.00 sec) 

Oto wyjaśnienia na zapytania (2) (który trwa wiecznie)

+----+-------------+-------+--------+-------------------------------+-----------+---------+--------------------------+----------+-------------+ 
| id | select_type | table | type | possible_keys     | key  | key_len | ref      | rows  | Extra  | 
+----+-------------+-------+--------+-------------------------------+-----------+---------+--------------------------+----------+-------------+ 
| 1 | SIMPLE  | g  | range | bar_id,bar_id_2,someTime  | someTime | 4  | NULL      | 95022220 | Using where | 
| 1 | SIMPLE  | t  | eq_ref | PRIMARY,someField,bar_id  | PRIMARY | 4  | db.f.bar_id    |  1 | Using where | 
+----+-------------+-------+--------+-------------------------------+-----------+---------+--------------------------+----------+-------------+ 

Oto wyjaśnić na (4) (który ma indeks siły)

+----+-------------+-------+------+-------------------------------+-----------+---------+--------------------------+----------+-------------+ 
| id | select_type | table | type | possible_keys     | key  | key_len | ref      | rows  | Extra  | 
+----+-------------+-------+------+-------------------------------+-----------+---------+--------------------------+----------+-------------+ 
| 1 | SIMPLE  | t  | ref | someField      | someField | 1  | const     |  92 |    | 
| 1 | SIMPLE  | g  | ref | bar_id,bar_id_2,someTime  | bar_id | 4  | db.f.foo_id    | 10558024 | Using where | 
+----+-------------+-------+------+-------------------------------+-----------+---------+--------------------------+----------+-------------+ 

Pytanie brzmi: jak nauczyć MySQL używać właściwego indeksu? Zapytanie jest generowane przez ORM i nie jest ograniczone tylko do tych dwóch pól. Byłoby miło także uniknąć wielu zmian w zapytaniu (choć nie jestem pewien, czy dopasowanie wewnętrzne pasuje tutaj).

UPDATE:

mysql> create index index_name on Foo (bar_id, someTime); 

Po że zapytanie (2) wykonuje się w 0,00 sekundy.

+5

Proszę ** nigdy ** nie "WYBIERZ *", jeśli SELECT ma jakiekolwiek sprzężenia. Zamiast tego określ, którą gwiazdkę masz na myśli. Na przykład: "SELECT f. * FROM foo f JOIN bar b ..." jest w porządku. W przeciwnym razie nie jest jasne, które pola twoje '* pobiera, a to spowolni – mvp

+0

Użyłem' SELECT * 'tylko na przykład, w prawdziwym DB ORM generuje zapytanie bez *. –

Odpowiedz

4

Jeśli utworzysz indeks złożony dla foo(table_id, sometime), powinno to bardzo pomóc. Wynika to z tego, że serwer będzie mógł zawęzić zestaw wyników najpierw przez table_id, a następnie przez sometime.

Należy pamiętać, że podczas korzystania z LIMIT serwer nie gwarantuje, które wiersze zostaną pobrane, jeśli wiele z nich kwalifikuje się do ograniczenia WHERE. Technicznie, każde wykonanie może dać nieco inny wynik. Jeśli chcesz uniknąć niejednoznaczności, zawsze powinieneś używać ORDER BY, kiedy używasz LIMIT. Oznacza to jednak, że powinieneś być ostrożniejszy w tworzeniu odpowiednich indeksów.

+0

Obecnie mam 6 kolumn w Foo i 3 kolumny w barze, które mogą być włączone do 'where' w dowolnych możliwych kombinacjach. Czy powinienem usunąć bieżące indeksy 'foo (field1)', 'foo (field2)' itp. I zastąpić je 'foo (bar_id, field1)' i tak dalej? –

+0

Indeks złożony '(a, b)' jest dobry do wyszukiwania samego 'a' i na' (a, b) '(gdy znane są zarówno' a' i 'b'), ale nie w samym' b' - wymagałoby to utworzenia indeksu na '(b)'. Indeks "(b, a)" nie jest potrzebny, jeśli "(a, b)" już istnieje. Powinieneś także użyć '(a, b)' w określonej kolejności - najpierw najbardziej selektywnej. – mvp

+0

Twoje rozwiązanie działa jak urok, wielkie dzięki. –