Đây là bảng của tôi với ~ 10.000.000 hàng dữ liệu
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;
Đây là chỉ số chính
Vì vậy, khi tôi thực hiện truy vấn này:
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;
Tôi đã mong đợi nó sử dụng chỉ mục voter_timestamp
nhưng mysql chọn sử dụng thay thế này:
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
Và tôi đã nhận được 200-400ms thời gian truy vấn.
Nếu tôi buộc nó sử dụng đúng chỉ mục như:
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 có thể trả về kết quả sau 1-2ms
và đây là lời giải thích:
type:
ref
possible_keys:
voter_timestamp
key:
voter_timestamp
key_len:
4
ref:
const
rows:
18714
filtered:
1.00
Extra:
Using where
Vậy tại sao mysql không chọn voter_timestampchỉ mục cho truy vấn ban đầu của tôi?
Những gì tôi đã thử là analyze table votes, optimize table votesbỏ chỉ mục đó và thêm lại, nhưng mysql vẫn sử dụng chỉ mục sai. không hiểu vấn đề là gì
(voter_id, updated_at). Một chỉ số khác sẽ là (voter_id, subject_name, updated_at)hoặc (subject_name, voter_id, updated_at)(không có tỷ lệ).
subject_name='medium' and rate=1)
LIMIThoặc thậm chí ORDER BYtrừ khi chỉ mục đầu tiên thỏa mãn tất cả các bộ lọc. Nghĩa là, không có 4 cột đầy đủ, nó sẽ thu thập tất cả các hàng có liên quan, sắp xếp tất cả chúng, sau đó chọn ra LIMIT. Với chỉ mục 4 cột, truy vấn có thể tránh sắp xếp và dừng lại sau khi chỉ đọc các LIMIThàng.

subject_name = "medium"phần đó, nó cũng có thể chọn đúng chỉ mục, không cần lập chỉ mụcrate