maugch
maugch

Reputation: 1318

complex query takes too much time transferring

the following query is very slow, I don't understand why. I have all id as indexes (some primary).

SELECT r.name as tool, r.url url ,r.id_tool recId, count(*) as count, r.source as source, 
     group_concat(t.name) as instrument
FROM tools r 
INNER JOIN
 instruments_tools ifr
ON ifr.id_tool = r.id_tool
INNER JOIN
 instrument t
ON t.id= ifr.id_instrument
WHERE t.id IN (433,37,362) AND t.source IN (1,2,3)
GROUP BY r.id_tool
ORDER BY count desc,rand() limit 10;

Locally on a Wampserver installation I have serious issues with transferring data. With Heidi I see two "Sending Data" of 2 resp 6 seconds. On a shared server, this is the important part I see:

| statistics                     | 0.079963 |
| preparing                      | 0.000028 |
| Creating tmp table             | 0.000037 |
| executing                      | 0.000005 |
| Copying to tmp table           | 7.963576 |
| converting HEAP to MyISAM      | 0.015790 |
| Copying to tmp table on disk   | 5.383739 |
| Creating sort index            | 0.015143 |
| Copying to group table         | 0.023708 |
| converting HEAP to MyISAM      | 0.014513 |
| Copying to group table         | 0.099595 |
| Sorting result                 | 0.034256 |

Considering that I'd like to improve the query (see LIMIT) or remove rand() and add weights, I'm a bit afraid I'm doing something very wrong.

Additional info: The tools table is 500.000 rows big, while the instruments around 6000. instruments_tools is around 3M rows. The query is to find which tool I can make with the instruments I have (by checking t.id IN(id of instruments). Group_concat(t.name) is a way to know which instrument is selected.

explain of the query:

+----+-------------+-------+--------+-------------------------+---------------+-------- -+----------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys           | key           | key_len     | ref                        | rows | Extra                                        |
+----+-------------+-------+--------+-------------------------+---------------+---------+----------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | t     | range  | PRIMARY                 | PRIMARY       | 4       | NULL                       |    3 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | ifr   | ref    | id_tool,id_instrument | id_instrument | 5       | mydb2.t.id          |  374 | Using where                                  |
|  1 | SIMPLE      | r     | eq_ref | PRIMARY                 | PRIMARY       | 4       | mydb2.ifr.id_tool |    1 |                                              |
+----+-------------+-------+--------+-------------------------+---------------+---------+----------------------------+------+----------------------------------------------+

Upvotes: 1

Views: 443

Answers (1)

Bill Karwin
Bill Karwin

Reputation: 562711

You need a compound index on the intersection table:

ALTER TABLE instruments_tools ADD KEY (id_instrument, id_tool);

The order of columns in that index is important!

What you're hoping for is that the joins will start with the instrument table, then look up the matching index entry in the compound index based on id_instrument. Then once it finds that index entry, it has the related id_tool for free. So it doesn't have to read the instrument_tools table at all, it only need to read the index entry. That should give the "Using index" comment in your EXPLAIN for the instruments_tools table.

That should help, but you can't avoid the temp table and filesort, because of the columns you're grouping by and sorting by cannot make use of an index.

You can try to make MySQL avoid writing the temp table to disk by increasing the size of memory it can use for temporary tables:

mysql> SET GLOBAL tmp_table_size = 256*1024*1024;      -- 256MB
mysql> SET GLOBAL max_heap_table_size = 256*1024*1024; -- 256MB

That figure is just an example. I have no idea how large it would have to be for the temp table in your case.

Upvotes: 1

Related Questions