Hier ist meine Tabelle mit ~ 10.000.000 Zeilendaten
CREATE TABLE `votes` (
`subject_name` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
`subject_id` int(11) NOT NULL,
`voter_id` int(11) NOT NULL,
`rate` int(11) NOT NULL,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`subject_name`,`subject_id`,`voter_id`),
KEY `IDX_518B7ACFEBB4B8AD` (`voter_id`),
KEY `subject_timestamp` (`subject_name`,`subject_id`,`updated_at`),
KEY `voter_timestamp` (`voter_id`,`updated_at`),
CONSTRAINT `FK_518B7ACFEBB4B8AD` FOREIGN KEY (`voter_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Hier sind die Index-Kardinalitäten
Also, wenn ich diese Abfrage mache:
SELECT SQL_NO_CACHE * FROM votes WHERE
voter_id = 1099 AND
rate = 1 AND
subject_name = 'medium'
ORDER BY updated_at DESC
LIMIT 20 OFFSET 100;
Ich hatte erwartet, dass es Index verwendet, voter_timestamp
aber MySQL wählt stattdessen diesen:
explain select SQL_NO_CACHE * from votes where subject_name = 'medium' and voter_id = 1001 and rate = 1 order by updated_at desc limit 20 offset 100;`
type:
index_merge
possible_keys:
PRIMARY,IDX_518B7ACFEBB4B8AD,subject_timestamp,voter_timestamp
key:
IDX_518B7ACFEBB4B8AD,PRIMARY
key_len:
102,98
ref:
NULL
rows:
9255
filtered:
10.00
Extra:
Using intersect(IDX_518B7ACFEBB4B8AD,PRIMARY); Using where; Using filesort
Und ich habe 200-400ms Abfragezeit.
Wenn ich es zwinge, den richtigen Index zu verwenden, wie:
SELECT SQL_NO_CACHE * FROM votes USE INDEX (voter_timestamp) WHERE
voter_id = 1099 AND
rate = 1 AND
subject_name = 'medium'
ORDER BY updated_at DESC
LIMIT 20 OFFSET 100;
MySQL kann die Ergebnisse in 1-2 ms zurückgeben
und hier ist die Erklärung:
type:
ref
possible_keys:
voter_timestamp
key:
voter_timestamp
key_len:
4
ref:
const
rows:
18714
filtered:
1.00
Extra:
Using where
Warum hat mysql den voter_timestampIndex für meine ursprüngliche Abfrage nicht ausgewählt?
Was ich versucht hatte, ist analyze table votes, optimize table votesdiesen Index zu löschen und erneut hinzuzufügen, aber MySQL verwendet immer noch den falschen Index. nicht ganz verstehen, was das Problem ist.
(voter_id, updated_at). Ein anderer Index wäre (voter_id, subject_name, updated_at)oder (subject_name, voter_id, updated_at)(ohne die Rate).
subject_name='medium' and rate=1)
LIMIToder sogar zum, es ORDER BYsei denn, der Index erfüllt zuerst alle Filter. Das heißt, ohne die vollständigen 4 Spalten werden alle relevanten Zeilen gesammelt, alle sortiert und dann die ausgewählt LIMIT. Mit dem 4-Spalten-Index kann die Abfrage die Sortierung vermeiden und anhalten, nachdem nur die LIMITZeilen gelesen wurden .

subject_name = "medium"Teil entferne, kann es auch den richtigen Index auswählen, ohne dass ein Index erforderlich istrate