2008-10-09 32 views
15

Tôi đã lược tả một số truy vấn trong một ứng dụng tôi đang làm việc và tôi đã truy vấn một truy vấn đang truy xuất nhiều hàng hơn mức cần thiết, tập kết quả được cắt bớt trong mã ứng dụng.Còn lại Tham gia hoạt động tốt hơn Tham gia nội bộ?

Thay đổi LEFT JOIN thành INNER JOIN đã cắt bộ kết quả thành chỉ những gì cần thiết và có lẽ sẽ có hiệu suất cao hơn (vì ít hàng hơn được chọn). Trong thực tế, truy vấn LEFT JOIN'ed đã vượt trội hơn INNER JOIN'ed, mất một nửa thời gian để hoàn thành.

LEFT JOIN: (127 tổng số hàng, Query mất 0,0011 giây)

INNER JOIN: (10 hàng tổng, Query mất 0,0024 giây)

(Tôi chạy các truy vấn nhiều lần và đó là những trung bình) .

Chạy GIẢI THÍCH trên cả hai cho thấy không có gì để giải thích sự khác biệt hiệu suất:

Đối với INNER JOIN:

id select_type  table type possible_keys key  key_len  ref  rows  Extra 
1 SIMPLE contacts  index  NULL  name  302  NULL   235 Using where 
1 SIMPLE lists   eq_ref  PRIMARY  PRIMARY  4 contacts.list_id  1 
1 SIMPLE lists_to_users eq_ref  PRIMARY  PRIMARY  8 lists.id,const 1  
1 SIMPLE tags   eq_ref  PRIMARY  PRIMARY  4 lists_to_users.tag_id 1  
1 SIMPLE users   eq_ref  email_2  email_2  302  contacts.email 1 Using where 

Đối với LEFT JOIN:

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 SIMPLE   contacts index  NULL  name  302  NULL 235  Using where 
1 SIMPLE  lists  eq_ref  PRIMARY  PRIMARY  4 contacts.list_id 1  
1 SIMPLE lists_to_users eq_ref  PRIMARY  PRIMARY  8 lists.id,const 1  
1 SIMPLE   tags  eq_ref  PRIMARY  PRIMARY  4 lists_to_users.tag_id 1  
1 SIMPLE  users  eq_ref  email_2  email_2  302  contacts.email 1 

Và truy vấn bản thân:

SELECT `contacts`.*, `lists`.`name` AS `group`, `lists`.`id` AS `group_id`, `lists`.`shared_yn`, `tags`.`name` AS `context`, `tags`.`id` AS `context_id`, `tags`.`color` AS `context_color`, `users`.`id` AS `user_id`, `users`.`avatar` 
FROM `contacts` 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.list_id=lists.id AND lists_to_users.user_id='1' AND lists_to_users.creator='1' 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 
INNER JOIN `users` ON users.email=contacts.email 
WHERE (contacts.user_id='1') 
ORDER BY `contacts`.`name` ASC 

(Điều khoản mà tôi đang nói đến là INNER JOIN cuối cùng trên bảng 'người dùng')

Truy vấn chạy trên cơ sở dữ liệu MySQL 5.1, nếu nó tạo sự khác biệt.

Có ai có manh mối về lý do truy vấn LEFT JOIN'ed hoạt động tốt hơn INNER JOIN'ed trong trường hợp này không?

CẬP NHẬT: Do đề xuất của Tomalak rằng các bảng nhỏ tôi đang sử dụng đã làm cho INNER JOIN phức tạp hơn, tôi đã tạo một cơ sở dữ liệu thử nghiệm với một số dữ liệu giả định. Bảng 'người dùng' là 5000 hàng và bảng địa chỉ liên hệ là ~ 500.000 hàng. Kết quả là như nhau (cũng là thời gian đã không thay đổi đó là đáng ngạc nhiên khi bạn xem xét rằng các bảng lớn hơn nhiều bây giờ).

Tôi cũng chạy ANALYZE và OPTIMIZE trên bảng danh bạ. Không tạo ra bất kỳ sự khác biệt rõ rệt nào.

+0

Bạn đã thử đặt tham gia bên trong trước? –

+0

Tôi có, nó tăng tốc truy vấn đó lên 20%, nhưng vẫn chậm hơn LEFT JOIN –

+0

Cố gắng buil mỗi truy vấn tuần tự (tham gia một bảng, đo lường, tham gia tiếp theo, v.v.) Có thể điều này giúp bạn xác định hoạt động chậm . – Tomalak

Trả lời

6

Có thể do INNER JOIN phải kiểm tra từng hàng trong cả hai bảng để xem liệu giá trị cột (email trong trường hợp của bạn) có phù hợp hay không. LEFT JOIN sẽ trả về tất cả từ một bảng bất kể. Nếu nó được lập chỉ mục thì nó sẽ biết phải làm gì nhanh hơn nữa.

+0

Tôi đã thử sử dụng chỉ mục trên cột email và chỉ mục kết hợp trên tên + cột email, nhưng kế hoạch thực hiện truy vấn vẫn giữ nguyên –

+0

Điều đó sẽ giúp cả INNER và LEFT tham gia I đoán, vì vậy tôi sẽ không nghĩ rằng nó sẽ làm cho một nhanh hơn so với khác bằng cách làm như vậy. – HAdes

+3

Kết nối bên trong quét một bảng và tìm các hàng phù hợp trong bảng khác, lý tưởng sử dụng và lập chỉ mục cho điều đó. Nó không phải kiểm tra mỗi hàng trong cả hai bảng như bạn đề nghị. – Tomalak

4

Cardinality bảng có ảnh hưởng đến trình tối ưu hóa truy vấn. Tôi đoán các bảng nhỏ như bạn đã làm cho bên trong tham gia hoạt động phức tạp hơn. Ngay sau khi bạn có nhiều hồ sơ hơn so với máy chủ DB sẵn sàng để giữ trong bộ nhớ, các bên trong tham gia có lẽ sẽ bắt đầu tốt hơn việc tham gia bên trái.

+0

Điều đó thật thú vị. Tôi sẽ phải kiểm tra trên một tập lớn hơn và xem nó có thực hiện cách bạn mô tả nó hay không. –

+0

Tôi đã chạy lại với nhiều bảng lớn hơn và kết quả giống nhau. –

+0

+1 trên câu trả lời @ Eran Galperin ive đọc ghi chú của bạn về câu hỏi của bạn và những bảng bạn nói về, không phải là "lớn" ở tất cả. Với phần cứng ngày nay, bạn cần bảng với hàng triệu hàng, khi chúng ta nói về những người bạn đời lớn. – kommradHomer

2

imo bạn đang rơi vào cạm bẫy được gọi là tối ưu hóa sớm. Trình tối ưu hóa truy vấn là những thứ không ổn định. Đề nghị của tôi, là để di chuyển cho đến khi bạn có thể xác định chắc chắn rằng một tham gia cụ thể là có vấn đề.

+1

Đây không phải là về tối ưu hóa, đây là về sự hiểu biết tại sao truy vấn hoạt động theo một cách nhất định. –

-3

LEFT JOIN đang trả về nhiều hàng hơn INNER JOIN vì 2 giá trị này khác nhau.
Nếu LEFT JOIN không tìm thấy mục nhập có liên quan trong bảng mà nó đang tìm kiếm, nó sẽ trả về NULL cho bảng.
Nhưng nếu INNER JOIN không tìm thấy mục nhập có liên quan, nó sẽ không trả lại tất cả toàn bộ hàng.

Nhưng đối với câu hỏi của bạn, bạn đã bật query_cache chưa? Thử chạy truy vấn với

SELECT SQL_NO_CACHE `contacts`.*, ... 

Khác hơn thế, tôi muốn cư các bảng với nhiều dữ liệu hơn, chạy

ANALYZE TABLE t1, t2; 
OPTIMIZE TABLE t1, t2; 

Và xem những gì sẽ xảy ra.

+0

Tất nhiên, phép nối bên trái trả về nhiều hàng hơn, đó không phải là điểm của câu hỏi. Tại sao nó chạy nhanh hơn NHƯNG khi trở về nhiều hàng hơn là những gì boggles tôi –

12

Nếu bạn nghĩ rằng việc triển khai LEFT JOIN là INNER JOIN + công việc khác, thì kết quả này gây nhầm lẫn. Điều gì xảy ra nếu việc thực hiện INNER JOIN là (LEFT JOIN + filtering)? Ah, giờ đã rõ rồi.

Trong kế hoạch truy vấn, sự khác biệt duy nhất là: người dùng ... thêm: sử dụng nơi. Điều này có nghĩa là lọc. Có bước lọc bổ sung trong truy vấn với tham gia bên trong.


Đây là loại lọc khác thường được sử dụng trong mệnh đề where. Thật đơn giản để tạo chỉ mục trên A để hỗ trợ hành động lọc này.

SELECT * 
FROM A 
WHERE A.ID = 3 

xem xét truy vấn này:

SELECT * 
FROM A 
    LEFT JOIN B 
    ON A.ID = B.ID 
WHERE B.ID is not null 

truy vấn này tương đương với bên tham gia. Không có chỉ mục trên B sẽ giúp hành động lọc. Lý do là mệnh đề where được nêu một tình trạng trên là kết quả của sự tham gia, thay vì một điều kiện trên B.

+0

Tôi nhận thức được sự khác biệt giữa một tham gia trái và một tham gia bên trong. Bạn có thể nói tương tự về mệnh đề WHERE, tuy nhiên các truy vấn được lọc với mệnh đề where thường tốn ít thời gian hơn để tính toán. –

+0

Tôi đọc những gì bạn đã thêm, và mặc dù tôi nghĩ rằng bạn có thể đang ở một cái gì đó với bước lọc bổ sung, tôi nghĩ rằng bạn đang tắt mục tiêu là tại sao. Có một chỉ mục trên cột lọc bổ sung 'email' (được sử dụng), do đó, nó phải đủ nhanh để cải thiện hiệu suất. –

+1

Có, chỉ mục trên email không giúp việc tham gia trái. Không, chỉ mục trên email không cho phép lọc nhanh các kết quả đăng bài. –

0

Hãy thử điều này:

SELECT `contacts`.*, `lists`.`name` AS `group`, `lists`.`id` AS `group_id`, `lists`.`shared_yn`, `tags`.`name` AS `context`, `tags`.`id` AS `context_id`, `tags`.`color` AS `context_color`, `users`.`id` AS `user_id`, `users`.`avatar` 
FROM `contacts` 
INNER JOIN `users` ON contacts.user_id='1' AND users.email=contacts.email 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.user_id='1' AND lists_to_users.creator='1' AND lists_to_users.list_id=lists.id 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 
ORDER BY `contacts`.`name` ASC 

Điều đó sẽ cung cấp cho bạn một hiệu năng cao hơn vì:

  • Bạn đặt tất cả các kết nối bên trong trước khi mọi tham gia "trái" hoặc "phải" xuất hiện. Bộ lọc này lọc ra một số bản ghi trước khi áp dụng các kết nối bên ngoài tiếp theo
  • Mạch ngắn của toán tử "AND" (thứ tự của các vấn đề "VÀ"). Nếu so sánh giữa các cột và các chữ cái sai, nó sẽ không thực hiện quét bảng yêu cầu để so sánh giữa các bảng PK và FKs

Nếu bạn không tìm thấy bất kỳ cải thiện hiệu suất nào, hãy thay thế tất cả columnset cho "COUNT (*)" và thực hiện các kiểm tra bên trái/bên trong của bạn. Bằng cách này, bất kể truy vấn, bạn sẽ chỉ truy xuất 1 hàng duy nhất với 1 cột đơn (số đếm), vì vậy bạn có thể loại bỏ số byte trả về là nguyên nhân làm chậm truy vấn của bạn:

SELECT COUNT(*) 
FROM `contacts` 
INNER JOIN `users` ON contacts.user_id='1' AND users.email=contacts.email 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.user_id='1' AND lists_to_users.creator='1' AND lists_to_users.list_id=lists.id 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 

Chúc may mắn

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