Dựa trên EXPLAIN
đầu ra trong câu hỏi của bạn, bạn đã có tất cả các chỉ mục mà truy vấn nên đang sử dụng, cụ thể là:
CREATE INDEX idx_zip_from_distance
ON zipcode_distances (zipcode_from, distance, zipcode_to);
CREATE INDEX idx_zipcode ON venues (zipcode, id);
CREATE INDEX idx_venue_id ON events (venue_id);
(Tôi không chắc về tên chỉ mục của bạn liệu idx_zip_from_distance
thực sự bao gồm zipcode_to
cột. Nếu không, bạn nên thêm nó để làm cho nó trở thành chỉ mục bao gồm
. Ngoài ra, tôi đã bao gồm venues.id
trong idx_zipcode
để hoàn thiện, nhưng, giả sử đó là khóa chính của bảng và bạn đang sử dụng InnoDB, thì nó sẽ tự động được đưa vào.)
Tuy nhiên, có vẻ như MySQL đang chọn một kế hoạch truy vấn khác, và có thể là không tối ưu, nơi nó quét qua tất cả các sự kiện, tìm địa điểm và mã zip của chúng và chỉ sau đó lọc kết quả theo khoảng cách. Điều này có thể là kế hoạch truy vấn tối ưu, nếu bản số của bảng sự kiện đủ thấp, nhưng từ thực tế là bạn đang hỏi câu hỏi này, tôi cho rằng không phải vậy.
Một lý do cho kế hoạch truy vấn tối ưu có thể thực tế là bạn có quá nhiều chỉ mục gây nhầm lẫn cho người lập kế hoạch. Ví dụ, bạn có thực sự không cần cả ba chỉ mục đó trên bảng mã zip, cho rằng dữ liệu mà nó lưu trữ có lẽ là đối xứng? Cá nhân tôi chỉ đề xuất chỉ mục mà tôi đã mô tả ở trên, cộng với một chỉ mục duy nhất (cũng có thể là khóa chính, nếu bạn không có chỉ mục nhân tạo) trên (zipcode_to, zipcode_from)
(tốt nhất là theo thứ tự đó, để thỉnh thoảng có bất kỳ truy vấn nào trên zipcode_to=?
có thể tận dụng nó).
Tuy nhiên, dựa trên một số thử nghiệm tôi đã thực hiện, tôi nghi ngờ vấn đề chính tại sao MySQL chọn sai kế hoạch truy vấn chỉ đơn giản là do các bản số tương đối của bảng của bạn. Có lẽ là zipcode_distances
thực tế của bạn bảng là rất lớn và MySQL không đủ thông minh để nhận ra có bao nhiêu điều kiện trong WHERE
thực sự thu hẹp nó lại.
Nếu vậy, cách khắc phục tốt nhất và đơn giản nhất có thể là chỉ cần buộc MySQL để sử dụng các chỉ mục bạn muốn :
select
*
from
zipcode_distances z
FORCE INDEX (idx_zip_from_distance)
inner join
venues v
FORCE INDEX (idx_zipcode)
on z.zipcode_to=v.zipcode
inner join
events e
FORCE INDEX (idx_venue_id)
on v.id=e.venue_id
where
z.zipcode_from='92108' and
z.distance <= 5
Với truy vấn đó, bạn thực sự sẽ nhận được kế hoạch truy vấn mong muốn. (Bạn cần FORCE INDEX
tại đây, vì chỉ với USE INDEX
người lập kế hoạch truy vấn vẫn có thể quyết định sử dụng quét bảng thay vì chỉ mục được đề xuất, không đạt được mục đích. Tôi đã có điều này xảy ra khi tôi thử nghiệm điều này lần đầu tiên.)
Ps. Đây là bản trình diễn về SQLize, cả với
và không có
FORCE INDEX
, chứng minh vấn đề.