We have a MariaDB table (stories) with more than 1TB of data, periodically running a query that fetches recently added rows for indexing somewhere else.
innodb_version: 5.6.36-82.1
version : 10.1.26-MariaDB-0+deb9u1
The query works just fine when the query optimizer decides on using the secondary index to do a range walk through (in buckets of a 1000):
explain extended SELECT stories.item_guid
FROM `stories`
WHERE (updated_at >= '2018-09-21 15:00:00')
AND (updated_at <= '2018-09-22 05:30:00')
ORDER BY `stories`.`id` ASC
LIMIT 1000;
+------+-------------+---------+-------+-----------------------------+-----------------------------+---------+------+--------+----------+---------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+------+-------------+---------+-------+-----------------------------+-----------------------------+---------+------+--------+----------+---------------------------------------+
| 1 | SIMPLE | stories | range | index_stories_on_updated_at | index_stories_on_updated_at | 5 | NULL | 192912 | 100.00 | Using index condition; Using filesort |
+------+-------------+---------+-------+-----------------------------+-----------------------------+---------+------+--------+----------+---------------------------------------+
1 row in set, 1 warning (0.00 sec)
But occasionally, even with small differences in the data set (Note: the second timestamp difference with the query above, worth to mention that the whole table holds data for several years and holds several dozens millions of rows) decides to use the primary key index:
explain extended SELECT stories.item_guid
FROM `stories`
WHERE (updated_at >= '2018-09-21 15:00:00')
AND (updated_at <= '2018-09-22 06:30:00')
ORDER BY `stories`.`id` ASC
LIMIT 1000;
+------+-------------+---------+-------+-----------------------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+------+-------------+---------+-------+-----------------------------+---------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | stories | index | index_stories_on_updated_at | PRIMARY | 8 | NULL | 240259 | 83.81 | Using where |
+------+-------------+---------+-------+-----------------------------+---------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
Causing it to walk through the whole primary key index (I guess sequentially) to then filter on updated_at field taking several hours to complete instead.
The query was created by the ORM ActiveRecord and is probably far from ideal. As a workaround solution we came up with a couple of manually crafted queries, that moves the ORDER BY stories.id out, and/or using use/force index to avoid the PK since we are really filtering our data set by updated_at.
What I'm interested in understanding here is how/why the query optimizer chooses that execution plan? I understand that query optimizer uses index/table statistics for taking that decision, but in this case, and If my understanding is correct about how innodb works, this is pretty clear that walking through a huge PK while not using any PK id for filtering is not ideal.
I'm essentially trying to understand where that "bad" decision comes from, what statistics or unknown variables are used, so as not to end up with the good plan (the one that is often chosen and which is orders of magnitude faster).
Feel free to correct any of my assumptions as I'm definitively not a DBA expert.
ORDER BYwithLIMITquery planning doesn't use this information as much as it should in query planning. – danblack Sep 22 '18 at 23:26stories.idfrom your first query (i.e. add it to the result), and then in the second query add a criteriastories.id > {prev_max}. This is more efficient thanOFFSETclauses. This might hint the query back to using the PK, which might be ok if most stories are updated/(created?) at the same time near the same story id. – danblack Sep 22 '18 at 23:31SHOW CREATE TABLE? Are there 48M rows in the table? And only 0.2M rows are in the range? – Rick James Oct 10 '18 at 00:19