Khi nào sử dụng STRAIGHT_JOIN với MySQL


88

Tôi vừa có một truy vấn khá phức tạp mà tôi đang làm việc và mất 8 giây để chạy. EXPLAIN đang hiển thị một thứ tự bảng kỳ lạ và các chỉ mục của tôi không được sử dụng ngay cả với gợi ý FORCE INDEX. Tôi bắt gặp từ khóa tham gia STRAIGHT_JOIN và bắt đầu thay thế một số từ khóa INNER JOIN bằng từ khóa này. Tôi nhận thấy tốc độ cải thiện đáng kể. Cuối cùng, tôi vừa thay thế tất cả các từ khóa INNER JOIN của mình bằng STRAIGHT_JOIN cho truy vấn này và nó hiện chạy trong 0,01 giây.

Câu hỏi của tôi là khi nào bạn sử dụng STRAIGHT_JOIN và khi nào bạn sử dụng INNER JOIN? Có lý do gì để không sử dụng STRAIGHT_JOIN nếu bạn đang viết các truy vấn tốt?

Câu trả lời:


73

Tôi không khuyên bạn nên sử dụng STRAIGHT_JOIN mà không có lý do chính đáng. Kinh nghiệm của riêng tôi là trình tối ưu hóa truy vấn MySQL chọn một kế hoạch truy vấn kém thường xuyên hơn tôi muốn, nhưng không đủ thường xuyên để bạn chỉ nên bỏ qua nó nói chung, đó là điều bạn sẽ làm nếu bạn luôn sử dụng STRAIGHT_JOIN.

Khuyến nghị của tôi là để tất cả các truy vấn dưới dạng THAM GIA thông thường. Nếu bạn phát hiện ra rằng một truy vấn đang sử dụng kế hoạch truy vấn phụ tối ưu, trước tiên tôi khuyên bạn nên thử viết lại hoặc cấu trúc lại truy vấn một chút để xem liệu trình tối ưu hóa có chọn kế hoạch truy vấn tốt hơn không. Ngoài ra, đối với innodb ít nhất, hãy đảm bảo rằng không chỉ thống kê chỉ mục của bạn đã lỗi thời ( BẢNG PHÂN TÍCH ). Điều đó có thể khiến trình tối ưu hóa chọn một kế hoạch truy vấn kém. Các gợi ý về trình tối ưu hóa thường là phương sách cuối cùng của bạn.

Một lý do khác để không sử dụng gợi ý truy vấn là phân phối dữ liệu của bạn có thể thay đổi theo thời gian hoặc tính chọn lọc chỉ mục của bạn có thể thay đổi, v.v. khi bảng của bạn phát triển. Các gợi ý truy vấn của bạn hiện là tối ưu, có thể trở nên tối ưu theo thời gian. Nhưng trình tối ưu hóa sẽ không thể điều chỉnh kế hoạch truy vấn do các gợi ý hiện đã lỗi thời của bạn. Bạn sẽ linh hoạt hơn nếu bạn cho phép trình tối ưu hóa đưa ra quyết định.


59
Câu trả lời này không thực sự giải thích khi nào sử dụng straight_join .
Pacerier

23

Từ tham chiếu MySQL JOIN :

"STRAIGHT_JOIN tương tự như JOIN, ngoại trừ việc bảng bên trái luôn được đọc trước bảng bên phải. Điều này có thể được sử dụng cho (một vài) trường hợp mà trình tối ưu hóa kết hợp đặt các bảng không đúng thứ tự."


27
Cảm ơn, nhưng tôi đã đọc hướng dẫn sử dụng MySQL về nó. Hy vọng cho một số giải thích thêm.
Greg

20

Đây là một kịch bản mới xuất hiện gần đây tại nơi làm việc.

Xét ba bảng A, B, C.

A có 3.000 hàng; B có 300.000.000 hàng; và C có 2.000 hàng.

Các khóa ngoại được định nghĩa: B (a_id), B (c_id).

Giả sử bạn có một truy vấn giống như sau:

select a.id, c.id
from a
join b on b.a_id = a.id
join c on c.id = b.c_id

Theo kinh nghiệm của tôi, MySQL có thể chọn C -> B -> A trong trường hợp này. C nhỏ hơn A và B là rất lớn, và tất cả chúng đều bằng nhau.

Vấn đề là MySQL không nhất thiết phải tính đến kích thước của phần giao nhau giữa (C.id và B.c_id) so với (A.id và B.a_id). Nếu phép nối giữa B và C trả về nhiều hàng bằng B, thì đó là một lựa chọn rất tồi; nếu bắt đầu bằng A sẽ lọc bớt B xuống nhiều hàng bằng A, thì đó sẽ là lựa chọn tốt hơn nhiều. straight_joincó thể được sử dụng để buộc đơn đặt hàng này như thế này:

select a.id, c.id
from a
straight_join b on b.a_id = a.id
join c on c.id = b.c_id

Bây giờ aphải được tham gia vào trước đây b.

Nói chung, bạn muốn thực hiện các phép nối của mình theo một thứ tự giảm thiểu số hàng trong tập hợp kết quả. Vì vậy, bắt đầu với một bảng nhỏ và tham gia sao cho kết quả tham gia cũng sẽ nhỏ, là lý tưởng. Mọi thứ sẽ diễn ra theo hình quả lê nếu bắt đầu với một chiếc bàn nhỏ và ghép nó vào một chiếc bàn lớn hơn thì kết thúc cũng chỉ lớn bằng chiếc bàn lớn.

Tuy nhiên, nó phụ thuộc vào số liệu thống kê. Nếu phân phối dữ liệu thay đổi, tính toán có thể thay đổi. Nó cũng phụ thuộc vào chi tiết triển khai của cơ chế tham gia.

Các trường hợp tồi tệ nhất mà tôi đã thấy đối với MySQL mà tất cả ngoại trừ straight_joingợi ý chỉ mục bắt buộc hoặc tích cực là các truy vấn phân trang trên nhiều dữ liệu theo thứ tự sắp xếp nghiêm ngặt với tính năng lọc ánh sáng. MySQL rất thích sử dụng các chỉ mục cho bất kỳ bộ lọc nào và tham gia nhiều loại; điều này có ý nghĩa bởi vì hầu hết mọi người không cố gắng sắp xếp toàn bộ cơ sở dữ liệu mà chỉ có một tập hợp con giới hạn các hàng đáp ứng với truy vấn và việc sắp xếp một tập hợp con giới hạn nhanh hơn nhiều so với lọc toàn bộ bảng, bất kể nó được sắp xếp hay không phải. Trong trường hợp này, đặt nối thẳng ngay sau bảng có cột được lập chỉ mục mà tôi muốn sắp xếp theo những thứ cố định.


Bạn sẽ sử dụng phép nối thẳng như thế nào để khắc phục sự cố?
Hannele

@Hannele straight_joinđánh giá bảng bên trái trước bên phải. Vì vậy, nếu bạn muốn đi từ A -> B -> Ctrong ví dụ của tôi, jointừ khóa đầu tiên có thể được thay thế bằng straight_join.
Barry Kelly

Ah gọn gàng. Nó sẽ là hữu ích để bao gồm mà làm ví dụ trong câu trả lời của bạn :)
Hannele

18

MySQL không thực sự tốt trong việc chọn thứ tự tham gia trong các truy vấn phức tạp. Bằng cách chỉ định một truy vấn phức tạp dưới dạng một hàm_xếp thẳng, truy vấn sẽ thực thi các phép nối theo thứ tự chúng được chỉ định. Bằng cách đặt bảng thành mẫu số chung nhỏ nhất trước tiên và chỉ định thẳng_xếp, bạn có thể cải thiện hiệu suất truy vấn.


11

STRAIGHT_JOIN, sử dụng mệnh đề này, bạn có thể kiểm soát JOINthứ tự: bảng nào được quét trong vòng ngoài và bảng nào ở vòng trong.


Vòng lặp bên ngoài và vòng lặp bên trong là gì?
Istiaque Ahmed

@IstiaqueAh Các bảng được liên kết bằng các vòng lồng nhau (lấy hàng đầu tiên từ bảng A và vòng lặp ném bảng B sau đó lấy hàng thứ hai ... và v.v. Đây là bảng A ở vòng ngoài)
Kế toán م

6

Tôi sẽ cho bạn biết tại sao tôi phải sử dụng STRAIGHT_JOIN:

  • Tôi gặp sự cố về hiệu suất với một truy vấn.
  • Đơn giản hóa truy vấn, truy vấn gần như hiệu quả hơn
  • Cố gắng tìm ra phần cụ thể nào đang gây ra vấn đề, tôi chỉ không thể. (2 kết hợp bên trái với nhau rất chậm và mỗi kết hợp nhanh độc lập)
  • Sau đó, tôi đã thực thi GIẢI THÍCH với cả truy vấn chậm và nhanh (thêm một trong các phép nối bên trái)
  • Điều đáng ngạc nhiên là MySQL đã thay đổi hoàn toàn các lệnh JOIN giữa 2 truy vấn.

Do đó, tôi đã buộc một trong các phép nối phải là thẳng_điểm để LÊN LÒNG liên kết trước đó được đọc trước. Điều này đã ngăn MySQL thay đổi thứ tự thực thi và hoạt động như một sự quyến rũ!


2

Theo kinh nghiệm ngắn gọn của tôi, một trong những tình huống STRAIGHT_JOINđã làm giảm truy vấn của tôi từ 30 giây xuống 100 mili giây là bảng đầu tiên trong kế hoạch thực thi không phải là bảng có thứ tự theo cột

-- table sales (45000000) rows
-- table stores (3) rows
SELECT whatever
FROM 
    sales 
    INNER JOIN stores ON sales.storeId = stores.id
ORDER BY sales.date, sales.id 
LIMIT 50;
-- there is an index on (date, id)

NẾU trình tối ưu hóa chọn để truy cập stores đầu tiên, nó sẽ gây ra Using index; Using temporary; Using filesortbởi vì

nếu ORDER BY hoặc GROUP BY chứa các cột từ các bảng khác với bảng đầu tiên trong hàng đợi nối, một bảng tạm thời sẽ được tạo.

nguồn

ở đây trình tối ưu hóa cần một chút trợ giúp bằng cách yêu cầu anh ta nhấn salestrước bằng cách sử dụng

sales STRAIGHT_JOIN stores

1
(Tôi đã chỉnh sửa câu trả lời của bạn.)
Rick James

2

Nếu truy vấn của bạn kết thúc bằng ORDER BY... LIMIT..., có thể là tối ưu để định dạng lại truy vấn để lừa trình tối ưu hóa thực hiệnLIMIT trước các JOIN.

(Câu trả lời này không chỉ áp dụng cho câu hỏi ban đầu về STRAIGHT_JOIN, cũng không áp dụng cho tất cả các trường hợp STRAIGHT_JOIN.)

Bắt đầu với ví dụ của @Accountant م , điều này sẽ chạy nhanh hơn trong hầu hết các tình huống. (Và nó không cần gợi ý.)

SELECT  whatever
    FROM  ( SELECT id FROM sales
                ORDER BY  date, id
                LIMIT  50
          ) AS x
    JOIN  sales   ON sales.id = x.id
    JOIN  stores  ON sales.storeId = stores.id
    ORDER BY  sales.date, sales.id;

Ghi chú:

  • Đầu tiên, 50 id được tìm nạp. Điều này sẽ đặc biệt nhanh chóng vớiINDEX(date, id) .
  • Sau đó, tham gia trở lại salescho phép bạn chỉ nhận được 50 "whatevers" mà không cần đưa chúng vào bảng tạm thời.
  • vì một truy vấn con, theo định nghĩa, không có thứ tự, là ORDER BY phải được lặp lại trong truy vấn bên ngoài. (Trình tối ưu hóa có thể tìm cách để tránh thực sự thực hiện một cách sắp xếp khác.)
  • Vâng, nó còn lộn xộn hơn. Nhưng nó thường nhanh hơn.

Tôi phản đối việc sử dụng hit bởi vì "Ngay cả khi nó nhanh hơn hôm nay, nó có thể không nhanh hơn vào ngày mai."


0

Tôi biết nó hơi cũ nhưng đây là một kịch bản, tôi đã thực hiện tập lệnh hàng loạt để điền vào một bảng nhất định. Tại một số thời điểm, truy vấn chạy rất chậm. Có vẻ như thứ tự tham gia không chính xác trên các bản ghi cụ thể:

  • Theo đúng thứ tự

nhập mô tả hình ảnh ở đây

  • Việc tăng id thêm 1 sẽ làm rối loạn đơn hàng. Lưu ý trường 'Thêm'

nhập mô tả hình ảnh ở đây

  • Sử dụng Straight_join sẽ khắc phục được sự cố

nhập mô tả hình ảnh ở đây

Thứ tự không chính xác chạy trong khoảng 65 giây trong khi sử dụng Straight_join chạy trong mili giây


-5
--use 120s, 18 million data
    explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d, tvassist_taid_all t
    WHERE d.taid = t.taid
      AND t.client_version >= '21004007'
      AND t.utdid IS NOT NULL
      AND d.recommend_day = '20170403'
    LIMIT 0, 10000

--use 3.6s repalce by straight join
 explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d
    STRAIGHT_JOIN 
      tvassist_taid_all t on d.taid = t.taid 
    WHERE 
     t.client_version >= '21004007'
       AND d.recommend_day = '20170403'

      AND t.utdid IS NOT NULL  
    LIMIT 0, 10000

3
Điều này không cung cấp cho bạn gần như đủ thông tin để tìm ra thời điểm liên kết thẳng là thích hợp.
Hannele
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.