2013-10-30 17 views
7

tôi đang cố gắng để truy vấn một bảng với một số (3) tại các khoản như:Số lượng tối đa của khoản TRÊN sử dụng chỉ mục trong MySQL

SELECT * 
FROM table 
WHERE 
    a IN (2884,5320) 
    AND 
    b IN ('a', 'b', 'c') 
    AND 
    c IN (1, 2, 3) 
    AND d='abcd' 
    AND date BETWEEN 0 AND 1383177599 

Bảng này được lập chỉ mục như index(a, b, c, d, date)

Tuy nhiên, khi tôi chạy một giải thích về truy vấn, người giải thích cho thấy rằng không có chỉ mục thích hợp để sử dụng. Điều này vẫn là trường hợp ngay cả khi tôi FORCE INDEX.

Nếu tôi thay đổi một trong những IN s trên để một = như

SELECT * 
FROM table 
WHERE 
    a = 2884 
    AND 
    b IN ('a', 'b', 'c') 
    AND 
    c IN (1, 2, 3) 
    AND d = 'abcd' 
    AND date BETWEEN 0 AND 1383177599 

MySQL sẽ cho phép tôi để buộc nó để sử dụng các chỉ số, nhưng sẽ chọn một chỉ số không bao gồm khác. Đây là trường hợp bất kể trong số các IN được đổi thành =.

Câu hỏi của tôi:

Có giới hạn với số lượng tại các khoản bạn có thể sử dụng cho một truy vấn được lập chỉ mục? Có điều gì rõ ràng tôi đang thiếu ở đây không?

Một vài điều cần biết về bảng:
9 GB, ~ 8.000.000 hàng. Nó chứa một cột văn bản có thể khá lớn (một trường JSON), nhưng cột này không phải là bất kỳ cột nào được truy vấn ở trên. Các điều khoản được hiển thị ở trên có thể lớn hơn đáng kể (200-300 mục)

Cảm ơn!

EDIT:
Đây là sản phẩm của giải thích trên các truy vấn (với FORCE INDEX) 1,"SIMPLE","table","ALL","correct_index",NULL,NULL,NULL,6977553,"Using where" đâu chỉ số chính xác là một trong những giải thích ở trên (index(a, b, c, d, date))

+0

Bạn có thể hiển thị kết quả giải thích không? – Ashalynd

+0

Yup, được thêm vào câu hỏi @Ashalynd – cmwright

Trả lời

2

Bạn không thể mong đợi nhiều hơn một cột được tìm kiếm qua chỉ mục cho các biến vị ngữ phạm vi như IN.

Thậm chí nếu bạn có một chỉ số nhiều cột (a, b, c, d, date), các cột tận cùng bên trái nên được cho bình đẳng predicates (=) và nhiều nhất là một cột có thể dành cho một vị tầm xa. Bất kỳ cột tiếp theo nào trong chỉ mục đều không hữu ích.

Ví dụ:

WHERE a = 2884 AND b = 'b' AND c IN (1, 2, 3) AND d = 'abcd' 

Vì vậy ab là vị bình đẳng, c là một vị phạm vi, và d là một vị bình đẳng.

Chạy GIẢI THÍCH trên truy vấn và lưu ý các cột lenref cho biết rằng bạn chỉ đang sử dụng hai cột đầu tiên của chỉ mục. Điều kiện cho d được thực hiện một cách khó khăn, bằng cách tìm kiếm tất cả các hàng được tìm thấy bởi chỉ mục trên ba cột đầu tiên.

  id: 1 
    select_type: SIMPLE 
     table: t 
     type: ref 
possible_keys: a 
      key: a 
     key_len: 7    <--- two columns' length 
      ref: const,const <--- only two values for index columns `a` and `b` 
     rows: 4 
     Extra: Using where; Using index 

Trong khi thay đổi c đến một vị bình đẳng cho phép tất cả bốn cột được sử dụng để tra cứu chỉ số:

WHERE a = 2884 AND b = 'b' AND c = 2 AND d = 'abcd' 

      id: 1 
    select_type: SIMPLE 
     table: t 
     type: ref 
possible_keys: a 
      key: a 
     key_len: 25      <--- four columns' length 
      ref: const,const,const,const <--- four values 
     rows: 2 
     Extra: Using where; Using index 

tôi nói về chi tiết này trong bài trình bày của tôi How to Design Indexes, Really.


Re bình luận của bạn:

là có cách nào để khắc phục điều này mà không cần phải viết lại mã?

Bạn có điểm rằng bạn chỉ có thể có một biến vị ngữ là có lợi từ chỉ mục. Bạn vẫn có thể có các biến vị ngữ khác trong mệnh đề WHERE của bạn, nhưng chúng không nhận được bất kỳ lợi ích nào từ một chỉ mục.

Nhưng đó không phải là công cụ giải quyết thỏa thuận, bởi vì nếu biểu thức một phạm vi bạn lập chỉ mục có thể giúp thu hẹp tìm kiếm xuống 99%, đó là chiến thắng. Sau đó, áp dụng các biểu thức khác cho các hàng phù hợp là chi phí chúng ta có thể sử dụng.

Trình tối ưu hóa sẽ cố gắng chọn chỉ mục hiệu quả nhất nếu có thể và điều này phần lớn chịu ảnh hưởng của chỉ số chọn lọc. Sau đó truy vấn sử dụng chỉ mục để thu hẹp tìm kiếm và chỉ tập hợp con các hàng vượt qua tìm kiếm đó được thử nghiệm với các điều kiện khác.

Đi khác nhìn vào truy vấn của bạn:

... WHERE 
a IN (2884,5320) 
AND 
b IN ('a', 'b', 'c') 
AND 
c IN (1, 2, 3) 
AND d='abcd' 
AND date BETWEEN 0 AND 1383177599 

Giả sử chúng ta biết rằng chỉ có 1% của các hàng phù hợp c IN (1,2,3), nhưng các điều khoản khác phù hợp hơn như 20-40% của các hàng trên trung bình.

Chúng tôi có thể lập chỉ mục cho vị từ bình đẳng, điều đó là ổn. Sau đó, chúng tôi sẽ chọn một cột khác cho chỉ mục vì tất cả các cụm từ khác là các biến vị ngữ phạm vi. Chúng tôi chọn cột được chọn lọc nhất: c. Do đó, chỉ mục tốt nhất là trên (d, c) và phải là theo thứ tự đó.

Bạn có thể có các truy vấn khác trong ứng dụng có các lựa chọn khác nhau cho các cột được tham chiếu trong mệnh đề WHERE và giá trị cụ thể mà chúng tôi đang tìm kiếm. Vì vậy, chúng tôi có thể cần một chỉ mục khác với một tập hợp các cột khác nhau hoặc thậm chí các cột giống nhau theo một thứ tự khác. Không cần nhiều chỉ mục, vì như tôi đã đề cập trong bản trình bày, các chỉ mục bạn cần tạo phụ thuộc vào các truy vấn bạn muốn tối ưu hóa.

+0

Đây là lời giải thích tuyệt vời @Bill, cảm ơn rất nhiều. Tôi đã trải qua bài trình bày đính kèm và nó có vẻ khá rõ ràng với tôi cách nó hoạt động ngay bây giờ. Về cơ bản những gì nó trông giống như là tôi đã đi qua một lỗ hổng thiết kế cơ bản trong truy vấn, cho nó để thực hiện trong bất kỳ cách nào bình thường tôi có thể không có nhiều hơn 1 'IN' (phạm vi) khoản. Có cách nào xung quanh điều này mà không cần phải viết lại mã? Bất kỳ thủ thuật MySQL nào tôi có thể sử dụng để tiếp tục với nhiều 'IN' hoặc tôi có bị mắc kẹt vào thời điểm này không? – cmwright

+0

Cảm ơn bạn đã phản hồi nhanh @Bill, thực sự đánh giá cao nó. Một câu hỏi cuối cùng: Nếu tôi được phép sử dụng một mệnh đề phạm vi duy nhất với chỉ mục, tại sao MySQL không cho tôi ép chỉ mục cho một cột ở trên? Có vẻ như nó sẽ cho phép tôi sử dụng chỉ số ở đó vì nó là cột ngoài cùng bên trái trong chỉ mục và là phạm vi đầu tiên. – cmwright

+0

Tôi không thể nói. Khi tôi kiểm tra nó, tôi có thể sử dụng nó trong trường hợp bạn mô tả. Tôi đang thử nghiệm trên MySQL 5.6.13. Tôi có thể đề nghị bạn tạo một thử nghiệm trên http://sqlfiddle.com –

Các vấn đề liên quan