views:

75

answers:

4

Setup:

mysql> create table test(id integer unsigned,s varchar(30));
Query OK, 0 rows affected (0.05 sec)

mysql> insert into test(id,s) value(1,'s');
Query OK, 1 row affected (0.00 sec)

mysql> insert into test(id,s) value(1,'tsr');
Query OK, 1 row affected (0.00 sec)

mysql> insert into test(id,s) value(1,'ts3r');
Query OK, 1 row affected (0.00 sec)

mysql> create index i_test_id on test(id);
Query OK, 3 rows affected (0.08 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> create index i_test_s on test(s);
Query OK, 3 rows affected (0.05 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql>  insert into test(id,s) value(21,'ts3r');
Query OK, 1 row affected (0.00 sec)

And then run this:

mysql> explain select * from test where id in (1) order by s desc;
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key       | key_len | ref   | rows | Extra                       |
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-----------------------------+
|  1 | SIMPLE      | test  | ref  | i_test_id     | i_test_id | 5       | const |    2 | Using where; Using filesort |
+----+-------------+-------+------+---------------+-----------+---------+-------+------+-----------------------------+
1 row in set (0.02 sec)

We can see it uses filesort instead of using the index on s,which will be slow when the selected result set is big.How to optimize it?

A: 

The index on id is being used to identify the rows to return. Depending on the version of MySQL you are using, it may only allow the use of one index per table, and the optimizer has determined it is more efficient to use the index for filtering the rows rather than for ordering.

Mark_Carrington
Does the latest version of MySQL allow the use of multiple index per table?
symfony
Yes, http://dev.mysql.com/doc/refman/5.0/en/index-merge-optimization.html, http://openquery.com/blog/mysql-50-index-merge-using-multiple-indexes
Daniel Vassallo
Seems it don't work for order by statement
symfony
It may also be dependent on your filter criteria - if the optimizer has determined from your sample statement that only a very small number of rows will be returned, it may decide not to bother using another index for the sort. Have a look what the explain statement returns when you have a representative number of records in your table and use real filter criteria as you may well get different results.
Mark_Carrington
I've populated the table and it's the same
symfony
If you are still using your "where id in (1)" filter clause, do you still have a similar small number of records that match, or do you now have many more matches? If it's still a small number of matches you might expect the query optimizer to ignore any indexes for the sort.If it is now returning a large number of results, you might want to change your index to cover both fields:create index i_test_id_s on test (id, s);MySQL can then use this same index to do both the filter on id and sort on s.
Mark_Carrington
Yes,`create index i_test_id_s on test (id, s); ` works.But I don't see how clustered index works?And i_test_id_s has nothing to do with index merge pointed out by @Daniel Vassallo,right?
symfony
A clustered index works in the same way as a regular index, but with the added optimization that the data records are stored on disk in the same order as they are sorted by the index. This gives you a performance boost when retrieving multiple adjacent rows due to decreased disk thrashing. An index becomes a clustered index when it is created on an InnoDB table as a unique index. If there is more than one unique index on the table, only the first one is the clustered index.
Mark_Carrington
Let me conclude,we will not benifit too much for queries with a `limit` clause,since there will only be limited disk thrashing;And for InnoDB,the unique index will not be clustered if a primary key already exists.Is there anything wrong with the two conclusions?
symfony
No, that sounds perfectly valid.
Mark_Carrington
+1  A: 

Sometimes MySQL does not use an index, even if one is available. One circumstance under which this occurs is when the optimizer estimates that using the index would require MySQL to access a very large percentage of the rows in the table.

From: MySQL 5.1 Reference Manual: How MySQL Uses Indexes

Daniel Vassallo
Is there a solution/workaround?
symfony
Test with real data, when selecting just 3 rows out of a small test table with almost no data, the overhead of looking up in an index would be greater than just scanning the 3 rows - mysql knows about such things.
nos
A: 

Create a clustered index on the column 'id'. Clustered index means a physical sort. That way I am guessing there wont be a filesort, when this query is invoked.

But a table can have only one clustered index. Hence , if you have another column that is a primary key for the table, you may not be able to create a clustered index on column 'id'. As primary keys by default are clustered.

The Machine
I don't understand what you mean by "As primary keys by default are clustered."
symfony
Primary keys are clustered indices. And a table can have only one clustered index. Obvious, since, the table data can be stored on the disk using only one sort-key.I dont have a MySql server. Can you try if my suggestion works ?As a shortcut you may create a primary key constraint on 'id'.
The Machine
I still don't understand what's a clustered index,isn't it the same as an index on several columns?Like `create ... i_index_col1_col2 on table(col1,col2)`
symfony
Clustered indexes apply to MySQL tables stored using the InnoDB storage engine only. Using a clustered index means the data is physically stored in the data file in the index order, which can improve performance when using an index based sort. MySQL uses the first unique index on the table as the clustered index.
Mark_Carrington
I guess you mean to create a unique index on `id` and `s`: `create unique index on test(id,s);`Is it right?
symfony
A clustered index on 'id' isn't going to help in this situation. If you are looking at a clustered index as a solution, you'd put a clustered index on 's', and a regular index on 'id'. The clustered index would save you on the sort, and a regular index would be just fine for the SELECT. Give it a try.
Marcus Adams
A: 

What version of MySQL are you on? Not until version 5 could MySQL use more than one index per table.

The choice of the indexes to use also depends on the size of the result set. With only two records returned in the result, it may not use the index anyway. For such small result sets, MySQL doesn't seem to mind sorting things manually.

However, what you could do to really help MySQL out, if this is a common query for you, is to add a compound index ('id', 's'). Basically, it's almost like your creating another little table that is always sorted by id then s, so no filesort would be required, and it would only need the one index, not two.

Marcus Adams
Is there any difference between these two compound indexes:`index('id','s')` and `index('s','id')`?Or say does it have direction?
symfony
Yes, the order definitely matters. With ('id', 's'), it orders first by 'id' and then by 's'. A telephone book might have this compound index ('lastname', 'firstname'). The order matters.
Marcus Adams
But I don't see the reason it matters,and I've had some comments about this:http://stackoverflow.com/questions/2500440/does-compound-index-have-direction-in-mysql/2500618#2500618
symfony