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_timestamp
Index für meine ursprüngliche Abfrage nicht ausgewählt?
Was ich versucht hatte, ist analyze table votes
, optimize table votes
diesen 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
)
LIMIT
oder sogar zum, es ORDER BY
sei 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 LIMIT
Zeilen gelesen wurden .
subject_name = "medium"
Teil entferne, kann es auch den richtigen Index auswählen, ohne dass ein Index erforderlich istrate