问题
I have a simple table with about 3 million records. I made the neccessary indexes, i also force the index PRIMARY but still doesnt work. It searches for nearly all 3 million rows instead of using the index to execute this one (record_id is INT auto-increment):
EXPLAIN SELECT record_id
FROM myrecords
FORCE INDEX (
PRIMARY )
ORDER BY record_id ASC
LIMIT 2955900 , 300
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE myrecords index NULL PRIMARY 4 NULL 2956200 Using index
The index is
Keyname Type Unique Packed Column Cardinality Collation Null
PRIMARY BTREE Yes No record_id 2956742 A No
I would like to know why this FORCED index is not being used the right way.
Without forcing index 'primary' both ASC and DESC tried, result is the same. Table has been repaired-optimized-analyzed. No luck.
query needs over a minute to execute!
WHAT I EXPECTED: query should proccess only 300 rows since that column is indexed. not nearly all 3 million of them as you can see in the first code-formatted block (scroll a little to the right)
回答1:
Index lookups are by value, not by position. An index can search for a value 2955900, but you're not asking for that. You're asking for the query to start at an offset of the 2955900th row in the table.
The optimizer can't assume that all primary key values are consecutive. So it's pretty likely that the 2955900th row has a value much higher than that.
Even if the primary key values are consecutive, you might have a WHERE condition that only matches, for example, 45% of the rows. In which case the id value on the 2955900th row would be way past the id value 2955900.
In other words, an index lookup of the id value 2955900 will not deliver the 2955900th row.
So MySQL can't use the index for a limit's offset. It must scan the rows to count them until it reaches offset+limit rows.
MySQL does have optimizations related to LIMIT, but it's more about stopping a table-scan once it has reached the number of rows to return. The optimizer may still report in an EXPLAIN plan that it expects it might have to scan the whole table.
A frequent misunderstand about FORCE INDEX is that it forces the use of an index. :-) In fact, if the query can't use an index (or if the available indexes don't have any benefit for this query), FORCE INDEX has no effect.
Re your comment:
Pagination is a frequent bane of data-driven web applications. Despite how common this feature is, it's not easy to optimize. Here are a few tips:
Why are you querying with offset 2955900? Do you really expect users to sift through that many pages? Most users give up after a few pages (exactly how many depends on the type of application and the data).
Reduce the number of queries. Your pagination function could fetch the first 5-10 pages, even if only it shows the first page to the user. Cache the other pages, with the assumption that the user will advance through a few pages. Only if they advance past the cached set of pages does your app have to do another query. You could even cache all 10 pages in Javascript on the client's browser so clicking "Next" is instantaneous for them (at least for those first few pages).
Don't put a "Last" button on any user interface, because people will click it out of curiosity. Notice Google has a "Next" button but not a "Last" button. So the UI itself discourages people from running inefficient queries with high offsets.
If the user is advancing one page at a time, use the highest id value returned in the previous page in the WHERE clause of the next page's query. I.e. the following does use the index, even with no FORCE INDEX hint:
SELECT * FROM thistable WHERE id > 544 LIMIT 20
来源:https://stackoverflow.com/questions/15144723/mysql-very-simple-select-id-order-by-limit-will-not-use-index-as-expected