Đâ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_timestamp
chỉ mục cho truy vấn ban đầu của tôi?
Những gì tôi đã thử là analyze table votes
, optimize table votes
bỏ 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
)
LIMIT
hoặc thậm chí ORDER BY
trừ 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 LIMIT
hà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