views:

314

answers:

5

Hello, I'm completely stumped on this one. For some reason when I sort this query by DESC it's super fast, but if sorted by ASC it's extremely slow.

This takes about 150 milliseconds:

SELECT posts.id
FROM posts USE INDEX (published)
WHERE posts.feed_id IN ( 4953,622,1,1852,4952,76,623,624,10 )
ORDER BY posts.published DESC
LIMIT 0, 50;

This takes about 32 seconds:

SELECT posts.id
FROM posts USE INDEX (published)
WHERE posts.feed_id IN ( 4953,622,1,1852,4952,76,623,624,10 )
ORDER BY posts.published ASC
LIMIT 0, 50;

The EXPLAIN is the same for both queries.

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  posts   index   NULL    published   5   NULL    50  Using where

I've tracked it down to "USE INDEX (published)". If I take that out it's the same performance both ways. But the EXPLAIN shows the query is less efficient overall.

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  posts   range   feed_id feed_id 4   \N  759 Using where; Using filesort

And here's the table.

CREATE TABLE `posts` (
  `id` int(20) NOT NULL AUTO_INCREMENT,
  `feed_id` int(11) NOT NULL,
  `post_url` varchar(255) NOT NULL,
  `title` varchar(255) NOT NULL,
  `content` blob,
  `author` varchar(255) DEFAULT NULL,
  `published` int(12) DEFAULT NULL,
  `updated` datetime NOT NULL,
  `created` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `post_url` (`post_url`,`feed_id`),
  KEY `feed_id` (`feed_id`),
  KEY `published` (`published`)
) ENGINE=InnoDB AUTO_INCREMENT=196530 DEFAULT CHARSET=latin1;

Is there a fix for this? Thanks!

+5  A: 

Your index is sorted desc so when you ask for ascending it needs to do a lot more work to bring it back in that order

SQLMenace
Thanks for the explanation. Is there a fix for this? I'm thinking about using 2 different queries, this one for DESC and a different one for ASC.
Pepper
If you are using mysql 5+ you should be able to define multiple indexes.
Aaron Harun
From http://dev.mysql.com/doc/refman/5.5/en/create-index.html :An index_col_name specification can end with ASC or DESC. These keywords are allowed for future extensions for specifying ascending or descending index value storage. Currently, they are parsed but ignored; index values are always stored in ascending order.So, how can I create an index that is sorted desc ?
a1ex07
A: 

how about flipping the WHERE condition?

I don't understand, can you clarify or provide an example?
Pepper
SELECT posts.idFROM posts USE INDEX (published)WHERE posts.feed_id IN ( 10,624,623,76,4952,1852,622,4953 )ORDER BY posts.published DESC;well ... post.created should have the exact same sorting
A: 

You want to add an index across (feed_id, published):

ALTER TABLE posts ADD INDEX (feed_id, published)

That'll make this query run best, and you won't need to force a particular index with USE INDEX.

beamrider9
This is what I had before, but on a table of 500000+ entries it was performing slowly. Forcing the index on published above is about 3x faster when ordered by DESC and 10x slower when ordered by ASC
Pepper
You tried this exact index? In that order? Define "slowly". 500,000+ rows is really not a big deal to MySQL (unless your server is ancient), and with an index across those two fields, this query should be all-index regardless of sort direction. That "Using filesort" in your one explain plan should tip you off - there's no reason that should happen when optimizing simple one-table queries like this.Also, what is "published"? Is there a good reason it's an int(12)?
beamrider9
I guess by "slowly" i mean slower than what I have with DESC sorting. It's doing a type: range, hitting 4000 rows instead of 50 and is using filesort.
Pepper
Oh and published is an unix epoch timestamp.
Pepper
Hmmm, yeah, I just tried it myself and saw what you reported. Apparently I'm no help on this. Just when you think you understand MySQL query optimization... :\
beamrider9
heh it's ok :) your answer is what I started with, and should be the best way to do it... sadly the results aren't as expected.
Pepper
A: 

I wouldn't suggest you create another index on the table; every time a row is inserted or deleted, each index on the table needs to be updated, slowing down INSERT queries.

The index is definitely what's slowing it down. Maybe you could try IGNORE-ing it:

SELECT posts.id
FROM posts IGNORE INDEX (published)
WHERE posts.feed_id IN ( 4953,622,1,1852,4952,76,623,624,10 )
ORDER BY posts.published ASC
LIMIT 0, 50;

Or, since the field is already KEYed, you might try the following:

SELECT posts.id
FROM posts USE KEY (published)
WHERE posts.feed_id IN ( 4953,622,1,1852,4952,76,623,624,10 )
ORDER BY posts.published ASC
LIMIT 0, 50;
amphetamachine
Thanks, USE KEY and USE INDEX seem to have the same result. And not using USE INDEX has the same result as IGNORE INDEX.
Pepper
+1  A: 

You could get your data set first, and then order it.

Something like

SELECT posts.id FROM (
SELECT posts.id
FROM posts USE INDEX (published)
WHERE posts.feed_id IN ( 4953,622,1,1852,4952,76,623,624,10 )
LIMIT 0, 50
)
order by postS.id ASC;

It should first use the index to find all records that satisfy your "where" statement, and the will order them. But the order would be performed in a smaller set. Give it a try and then tell us.

Best Regards.

santiagobasulto
Thanks, ill try it out.
Pepper
quick note, the subquery above needs an alias to work.
Pepper