2013-06-13 12 views
8

Ich habe Tabelle Foo mit 200 Millionen Datensätze und Tabelle Bar mit 1000 Datensätze, sie sind viele-zu-eins verbunden. Es gibt Indizes für die Spalten Foo.someTime und Bar.someField. Auch in Bar 900 Datensätze wurden von somefield 1, 100 haben somefield von 2.Mit Index auf innere Join-Tabelle in MySQL

(1) Diese Abfrage führt sofort:

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) Dieser dauert nur ewig (die einzige Änderung ist, 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) Aber wenn ich drop out where-Klausel auf Irgendwann, als er führt auch sofort:

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) Auch ich kann es beschleunigen, indem die Indexverwendung zwingt:

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) 

Hier ist die auf Abfrage erklären (2) (das dauert ewig)

+----+-------------+-------+--------+-------------------------------+-----------+---------+--------------------------+----------+-------------+ 
| 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 | 
+----+-------------+-------+--------+-------------------------------+-----------+---------+--------------------------+----------+-------------+ 

Hier ist die Erklärung auf (4) (welcher Kraftindex hat)

+----+-------------+-------+------+-------------------------------+-----------+---------+--------------------------+----------+-------------+ 
| 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 | 
+----+-------------+-------+------+-------------------------------+-----------+---------+--------------------------+----------+-------------+ 

Also ist die Frage, wie man MySQL lehrt, richtigen Index zu verwenden? Die Abfrage wird von ORM generiert und ist nicht nur auf diese beiden Felder beschränkt. Und es wäre auch schön, die Abfrage nicht viel zu ändern (obwohl ich mir nicht sicher bin, ob der innere Join hier passt).

UPDATE:

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

, dass nach der Abfrage (2) ausführt, in 0,00 sec.

+5

Bitte ** nie ** 'SELECT *' wenn Ihr SELECT irgendwelche Joins hat. Geben Sie stattdessen an, welcher Stern gemeint ist. Zum Beispiel ist 'SELECT f. * FROM foo f JOIN bar b ...' ok. Ansonsten ist nicht klar, welche Felder Ihr '*' holt, und es wird langsamer – mvp

+0

Ich habe 'SELECT *' nur zum Beispiel verwendet, in realen DB generiert ORM die Abfrage ohne *. –

Antwort

4

Wenn Sie zusammengesetzte Index für foo(table_id, sometime) erstellen, sollte es eine Menge helfen. Dies liegt daran, dass der Server das Ergebnis zuerst auf table_id und dann auf sometime eingrenzen kann.

Beachten Sie, dass bei Verwendung von LIMIT der Server nicht garantiert, welche Zeilen abgerufen werden, wenn viele für Ihre WHERE-Einschränkung geeignet sind. Technisch kann jede Ausführung ein leicht unterschiedliches Ergebnis liefern. Wenn Sie Mehrdeutigkeiten vermeiden möchten, sollten Sie immer ORDER BY verwenden, wenn Sie LIMIT verwenden. Dies bedeutet jedoch auch, dass Sie beim Erstellen geeigneter Indizes vorsichtiger sein sollten.

+0

Momentan habe ich 6 Spalten in Foo und 3 Spalten in Bar, die in 'wo' in allen möglichen Kombinationen enthalten sein können. Soll ich die aktuellen Indizes 'foo (field1)', 'foo (field2)' usw. entfernen und sie durch 'foo (bar_id, field1)' usw. ersetzen? –

+0

Zusammengesetzter Index '(a, b)' ist gut für die Suche nach 'a' allein und auf' (a, b) '(wenn sowohl' a' als auch 'b' bekannt sind), aber nicht nur' b' es würde die Erstellung eines Indexes für "(b)" erfordern. Der Index auf '(b, a)' wird nicht benötigt, wenn '(a, b)' bereits existiert. Außerdem sollten Sie '(a, b)' in einer bestimmten Reihenfolge verwenden - zuerst die selektivste Spalte. – mvp

+0

Ihre Lösung wirkt wie ein Zauber, vielen Dank. –