The table in question has millions of records. The following query is really slow (takes up to four minutes to perform), because MySQL picks the primary key as index.
SELECT
*
FROM
`activities`
WHERE
`integration_id` = 11
ORDER BY `id` DESC
LIMIT 10 OFFSET 2
The table has multiple indexes, including an index on integration. When forced, the use of this index reduces the query time to about a second:
SELECT
*
FROM
`activities`
USE INDEX (integration)
WHERE
`integration_id` = 11
ORDER BY `id` DESC
LIMIT 10 OFFSET 2
Using EXPLAIN, MySQL shows the following:
first query: (the slow one)
- key: PRIMARY
- key_len: 4
- rows: 5472
second query: (the fast one)
- key: integration
- key_len: 5
- rows: 24028
Seeing MySQL's explanation, I understand why it picks the primary key. However, in reality the amount of rows in the first query should be millions; and indeed is excruciatingly slow to execute.
Unfortunately I cannot just force the index in the query, because the query is dynamic (some other clauses can be included), and an ORM is used. So i'm looking for a solution in MySQL's configuration, if possible.
Extra info
I found altering the query in some cases resulted in MySQL picking the correct index. E.g. removing the ORDER BY clause, and replacing SELECT * with SELECT <insert every table column here>. I've no clue why. Removing the order is no option for me, and manually selecting every column is tricky with the ORM (apart from that, I would like to know why this fixes the index picking).
The EXPLAIN
- id: '1'
- select_type: 'SIMPLE'
- table: 'activities'
- type: 'index'
- possible_keys: 'integration_category,integration_level,integration_category_level,integration'
- key: PRIMARY
- key_len: 4
- ref: null
- rows: '5473'
- Extra Using where
SHOW CREATE TABLE
'CREATE TABLE `activities` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`content` text COLLATE utf8_unicode_ci NOT NULL,
`parameters` text COLLATE utf8_unicode_ci,
`input` text COLLATE utf8_unicode_ci,
`output` text COLLATE utf8_unicode_ci,
`response` text COLLATE utf8_unicode_ci,
`integration_id` int(10) unsigned DEFAULT NULL,
`level` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`category` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `integration_category` (`integration_id`,`category`),
KEY `integration_level` (`integration_id`,`level`),
KEY `integration_category_level` (`integration_id`,`category`,`level`),
KEY `integration` (`integration_id`),
CONSTRAINT `activities_integration_id_foreign` FOREIGN KEY (`integration_id`) REFERENCES `integrations` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=11262471 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'
id DESCto your index...ORDER BY 'id' ASC, to see if it would make any difference. It didn't.idis your (single-column) primary key and that the indexintegrationonly consists of that one column? (Maybe add thecreate tablefor your table). Your 2nd query should not even take close to a second with correct indexes. And please add the complete explain output, especially the extra-part and information about partitions if you use them. You might also try anoptimize table activities(although it should not have an effect here).id(which is the single column primary key),integration(which is one column as well). This integration column is used in every query. Furthermore users can filter on two more columns, that's why three more indexes are used:integration_level,integration_categoryandintegration_category_level. Since adding these indexes queries with these filters are very fast. The EXPLAIN:'1', 'SIMPLE', 'activities', 'index', 'integration_category,integration_level,integration_category_level,integration', 'PRIMARY', '4', NULL, '5473', 'Using where'