Tại sao MySQL chọn kế hoạch thực hiện này?


7

Tôi có hai truy vấn,

select some_other_column 
from `table` 
order by primary_index_column asc 
limit 4000000, 10;

select some_other_column 
from `table` 
order by secondary_index_column asc 
limit 4000000, 10;

Cả hai trả về 10 hàng; lần đầu tiên mất 2,74 giây và lần thứ hai mất 7,07 giây. some_other_columnkhông phải là một phần của bất kỳ chỉ số. primary_index_columnlà cột khóa chính; secondary_index_columncó chỉ số b-cây và số lượng thẻ là 200 (theo MySQL).

Đây là explainkết quả:

mysql> explain select some_other_column from `table` order by primary_index_column limit 4000000, 10;
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-------+
| id | select_type | table   | type  | possible_keys | key     | key_len | ref  | rows    | Extra |
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-------+
|  1 | SIMPLE      | table   | index | NULL          | PRIMARY | 4       | NULL | 4000010 |       |
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-------+

mysql> explain select some_other_column from `table` order by secondary_index_column limit 4000000, 10;
+----+-------------+---------+------+---------------+------+---------+------+---------+----------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows    | Extra          |
+----+-------------+---------+------+---------------+------+---------+------+---------+----------------+
|  1 | SIMPLE      | table   | ALL  | NULL          | NULL | NULL    | NULL | 4642945 | Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+---------+----------------+

Tại sao MySQL chọn kế hoạch thực hiện cụ thể cho truy vấn thứ hai? Tôi không hiểu tại sao nó có thể sử dụng chỉ mục cho truy vấn đầu tiên nhưng không sử dụng cho truy vấn thứ hai.

Câu trả lời:


7

Một cột được lập chỉ mục trong InnoDB luôn có một khóa bổ sung cho gen_clust_index (còn gọi là Chỉ mục cụm) được đính kèm. Điều này sẽ được duyệt qua truy vấn đầu tiên để đến hàng 4000000 theo thứ tự của chỉ mục. Vì nó là cột duy nhất được yêu cầu, nên việc truy cập vào bảng là không cần thiết.

Truy vấn thứ hai phải thu thập cột không được lập chỉ mục từ bảng cùng với cột được lập chỉ mục vào bảng tạm thời. Bảng tạm thời sau đó được sắp xếp trước khi trình bày cột không được lập chỉ mục dưới dạng đầu ra CHỌN.

Lưu ý một sự tương phản khác

  • Tổng số bàn là 4636881
  • Gói EXPLAIN cho truy vấn đầu tiên đi qua 4000010 khóa indexed_column. Không cần phải đọc 636871 phím cuối cùng.
  • Gói EXPLAIN cho truy vấn thứ hai đi qua 4636881 hàng được sắp xếp theo indexed_column. Đối với mỗi hàng chọn cột không được lập chỉ mục ra khỏi bảng, cột được lập chỉ mục (đã được sắp xếp theo chỉ mục) được tra cứu và đi cùng cho chuyến đi. Bảng tmp được sắp xếp theo cột được lập chỉ mục và mysqld sau đó loại bỏ 4000000 hàng đầu tiên, để lại 10 hàng. Tất cả sự tương tác giữa bảng và chỉ mục chỉ trong 10 hàng là nút cổ chai.

NHỮNG ĐIỀU

Trong cả hai trường hợp, truy vấn chỉ định số lượng hàng cần duyệt. Vì số lượng hàng trong bảng là 4636881, chúng ta sẽ dễ dàng mong đợi được quét toàn bộ. Sự tương phản trở nên rõ ràng khi Trình tối ưu hóa truy vấn MySQL quyết định nơi thực hiện quét toàn bộ.

  • Truy vấn đầu tiên chỉ tham chiếu một cột được lập chỉ mục trong danh sách CHỌN và mệnh đề WHERE. Trình tối ưu hóa truy vấn MySQL chọn thực hiện quét chỉ mục đầy đủ mà không cần liên hệ với bảng vì mọi thứ cần thiết đều nằm trong chỉ mục.
  • Truy vấn thứ hai đang tham chiếu một cột được lập chỉ mục trong mệnh đề WHERE. Tuy nhiên, nó phải vươn ra bảng để lấy cột không được lập chỉ mục tương ứng. Bộ kiểm tra truy vấn MySQL đã bị lật bởi truy vấn rằng nó không được sử dụng chỉ mục vì số lượng hàng dự kiến ​​sẽ đọc. Như một quy tắc chung cho bất kỳ RDBMS nào, nếu phải đọc hơn 5% bảng để hoàn thành một truy vấn, Trình tối ưu hóa truy vấn MySQL sẽ chỉ cần ném chỉ mục 'dưới bus' và thực hiện quét toàn bộ bảng .

Làm toán, đây là những gì trình tối ưu hóa Truy vấn MySQL tính toán:

  • 5% của 4636881 là 231844
  • Truy vấn thứ hai được lệnh để đọc 4000000 hàng, cao hơn 231844
  • Trình tối ưu hóa truy vấn MySQL nhận ra rằng sẽ có quá nhiều tương tác giữa bảng (vì cột không được lập chỉ mục) và chỉ mục (vì cột được lập chỉ mục) để có được dữ liệu cần thiết. Nó quyết định chỉ đọc bảng (vì cả hai cột được lập chỉ mục và không được lập chỉ mục đều nằm cùng nhau trong bảng) chứ không phải nảy qua lại giữa chúng.

Theo ý kiến ​​trung thực của tôi, với số lượng hàng của bảng, chỉ mục hiện tại của bảng và số lượng hàng được quy định bởi truy vấn, Trình tối ưu hóa truy vấn MySQL đã đưa ra quyết định chính xác .

SỰ GIỚI THIỆU

Tạo chỉ mục này

ALTER TABLE `table` ADD INDEX mynewndx (indexed_column,some_other_column);

và truy vấn thứ hai của bạn sẽ không bao giờ chạm vào bảng một lần nữa về phía trước. Trình tối ưu hóa truy vấn MySQL sẽ hoạt động hoàn toàn khác khi nhìn thấy chỉ mục mới này.


Trên thực tế, nó đọc tất cả 4636881 hàng đầu tiên trong tập tin tập tin như trong kế hoạch GIẢI THÍCH. CHỌN thực hiện việc bỏ qua bằng cách chỉ nhận được 10 hàng mà nó muốn cuối cùng.
RolandoMySQLDBA

Họ không tìm thấy các hàng giống nhau vì truy vấn đầu tiên chỉ đọc chỉ mục được nhóm và KHÔNG BAO GIỜ TOUCHES BẢNG. Truy vấn thứ hai phải liên hệ với chỉ mục cho mỗi hàng mà nó chạm vào trong các bảng. Nói một cách đơn giản, truy vấn thứ hai phải đọc bảng và chỉ mục.
RolandoMySQLDBA

Trên thực tế, các kế hoạch GIẢI THÍCH cho cả hai truy vấn có ý nghĩa hoàn toàn với tôi. Nó giống như một người bơi chỉ cần tôn trọng đại dương mà anh ta bơi vào. Tôi tin tưởng vào những con số mà nó đang báo cáo dựa trên số lượng hàng trong bảng và dựa trên các cột được triệu tập. Trong thực tế, tôi không có một đề nghị nào và tôi sẽ thêm nó vào câu trả lời của mình.
RolandoMySQLDBA

@Matt: Cả hai truy vấn của bạn có thể tốt hơn (và bất kỳ truy vấn nào cho vấn đề đó, đó là trình tối ưu hóa để quyết định điều đó) không sử dụng chỉ mục mà chỉ đọc toàn bộ bảng và tệp. Các truy vấn của bạn có LIMIT 10 OFFSET 4000000phần này, điều đó có nghĩa là công cụ SQL phải bằng cách nào đó (lập chỉ mục hoặc bằng cách khác) nhận được 4 triệu (và 10) hàng và loại bỏ 4 triệu (!) Đầu tiên trong số chúng. Đừng ngạc nhiên nếu sử dụng chỉ mục không xảy ra hoặc chậm.
ypercubeᵀᴹ

Tôi đã cập nhật câu trả lời của mình COMMON THINGSđể giải thích hành vi tối ưu hóa đằng sau các truy vấn của bạn.
RolandoMySQLDBA

0

Theo tài liệu của MySQL về tối ưu hóa order bycác truy vấn ,

Trong một số trường hợp, MySQL không thể sử dụng các chỉ mục để giải quyết ĐẶT HÀNG B [NG [...] những trường hợp này bao gồm:

  • [...]
  • Loại chỉ mục bảng được sử dụng không lưu trữ các hàng theo thứ tự. Ví dụ: điều này đúng với chỉ số HASH trong bảng NHỚ.

Hiểu biết của tôi về InnoDB là các hàng được lưu theo thứ tự theo khóa chính. Vì vậy, chúng không theo thứ tự cho bất kỳ chỉ mục phụ.


Đó không phải là một lời giải thích tốt về lý do tại sao chỉ số phụ không được sử dụng.
ypercubeᵀᴹ

@ypercube có vấn đề gì vậy?
Matt Fenwick

Ý tôi là có thông tin được lưu trữ trong bảng (tích lũy theo chỉ số phân cụm thường là PK) và trong các chỉ mục phụ. Nếu bạn có nghĩa là some_other_columnthông tin không được lưu trữ trong secondary_index_columnchỉ mục thì OK. Điều đó giải thích tại sao một chỉ số (bao trùm) (secondary_index_column, some_other_column), như được đề xuất bởi Rolando, có chứa thông tin và có thể hữu ích.
ypercubeᵀᴹ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.