Ini meja saya dengan ~ 10.000.000 data baris
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;
Berikut adalah kardinalitas indeks
Jadi ketika saya melakukan kueri ini:
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;
Saya mengharapkan itu menggunakan indeks voter_timestamp
tetapi mysql memilih untuk menggunakan ini sebagai gantinya:
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
Dan saya mendapat waktu permintaan 200-400ms.
Jika saya memaksanya untuk menggunakan indeks yang tepat seperti:
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 dapat mengembalikan hasilnya dalam 1-2ms
dan inilah penjelasannya:
type:
ref
possible_keys:
voter_timestamp
key:
voter_timestamp
key_len:
4
ref:
const
rows:
18714
filtered:
1.00
Extra:
Using where
Jadi mengapa mysql tidak memilih voter_timestamp
indeks untuk permintaan awal saya?
Apa yang saya coba adalah analyze table votes
,, optimize table votes
letakkan indeks itu dan tambahkan lagi, tetapi mysql masih menggunakan indeks yang salah. tidak begitu mengerti apa masalahnya.
(voter_id, updated_at)
. Indeks lain akan (voter_id, subject_name, updated_at)
atau (subject_name, voter_id, updated_at)
(tanpa kurs).
subject_name='medium' and rate=1
)
LIMIT
atau bahkan ORDER BY
kecuali indeks memenuhi semua penyaringan terlebih dahulu. Artinya, tanpa 4 kolom penuh, ia akan mengumpulkan semua baris yang relevan, mengurutkan semuanya, lalu memilih LIMIT
. Dengan indeks 4-kolom, query dapat menghindari jenis dan berhenti setelah membaca hanya satu LIMIT
baris.
subject_name = "medium"
bagian itu juga dapat memilih indeks yang tepat, tidak perlu indeksrate