2012-03-01 35 views
9

Tôi có một truy vấn cho tôi vấn đề và tôi không thể hiểu tại sao trình tối ưu hóa truy vấn của MySQL hoạt động theo cách của nó. Dưới đây là thông tin cơ bản:Tại sao MySQL không thể tối ưu hóa truy vấn này?

Tôi có 3 bảng. Hai là tương đối nhỏ và một là lớn.

Bảng 1 (rất nhỏ, 727 dòng):

CREATE TABLE ipa (
ipa_id int (11) AUTO_INCREMENT NOT NULL,
ipa_code int (11) DEFAULT NULL,
ipa_name varchar (100) DEFAULT NULL,
payorcode varchar (2) DEFAULT NULL,
compid int (11) DEFAULT '2'
PRIMARY KEY (ipa_id),
KEY ipa_code (ipa_code)) ENGINE = MyISAM

Bảng 2 (smallish, 59.455 dòng):

CREATE TABLE assign_ipa (
assignid int (11) NOT NULL AUTO_INCREMENT,
ipa_id int (11) NOT NULL,
userid int (11) NOT NUL L,
username varchar (20) DEFAULT NULL,
compid int (11) DEFAULT NULL,
PayorCode char (10) DEFAULT NULL
PRIMARY KEY (assignid),
UNIQUE KEY assignid (assignid, ipa_id),
KEY ipa_id (ipa_id)
) ENGINE = MyISAM

Bảng 3 (lớn, 24.711.730 hàng):

CREATE TABLE master_final (
IPA int (11) DEFAULT NULL,
MbrCt smallint (6) DEFAULT '0',
PayorCode varchar (4) DEFAULT 'WC',
KEY idx_IPA (IPA)
) ENGINE = MyISAM DEFAULT

Bây giờ cho truy vấn. Tôi đang làm một cách 3-tham gia bằng cách sử dụng hai bảng nhỏ đầu tiên về cơ bản tập hợp các bảng lớn trên một trong những giá trị được lập chỉ mục của nó. Về cơ bản, tôi nhận được một danh sách các ID cho người dùng, SJOnes và truy vấn tệp lớn cho những ID đó.

mysql> giải thích
SELECT master_final.PayorCode, sum (master_final.Mbrct) AS MbrCt
TỪ master_final
INNER JOIN ipa ON ipa.ipa_code = master_final.IPA
INNER JOIN assign_ipa ON ipa.ipa_id = assign_ipa.ipa_id
ĐÂU assign_ipa.username = 'SJones'
GROUP BY master_final.PayorCode, master_final.ipa \ G;
* ** * ** * ** * ** * 1. hàng * ** * ** * ** * * * *
id: 1
select_type: SIMPLE
bảng: master_final
loại: TẤT CẢ
possible_keys: idx_IPA
chính: NULL
key_len: NULL
ref: NULL
hàng:
thêm: Sử dụng tạm thời; Sử dụng filesort
* ** * ** * ** * ** * 2. hàng * ** * ** * ** * ** *
id: 1
select_type: SIMPLE
bảng: ipa
loại: ref
possible_keys: TIỂU, ipa_code
chính: ipa_code
key_len: 5
ref: wc_test.master_final.IPA
hàng: 1
tắm: Sử dụng nơi
* ** * ** * ** * ** * 3.hàng * ** * ** * ** * ** *
id: 1
select_type: SIMPLE
bảng: assign_ipa
loại: ref
possible_keys: ipa_id
khóa: ipa_id
key_len: 4
ref: wc_test.ipa .ipa_id
hàng: 37
Extra: (! như 30 phút) Sử dụng nơi
3 hàng trong set (0.00 sec)

Truy vấn này sẽ mãi mãi. Tuyên bố giải thích cho tôi biết tại sao, nó thực hiện quét toàn bộ bảng trên bảng lớn mặc dù có một chỉ số hoàn toàn tốt. Nó không sử dụng nó. Tôi không hiểu điều này. Tôi có thể xem xét truy vấn và thấy rằng nó chỉ cần truy vấn một vài ID từ bảng lớn. Nếu tôi có thể làm điều đó, tại sao không thể tối ưu hóa của MySQL làm điều đó?

Để minh họa, sau đây là các ID kết hợp với 'SJones':

mysql> select Tên truy nhập, ipa_id từ assign_ipa nơi username = 'SJones';
+ ---------- + -------- +
| tên người dùng | ipa_id |
+ ---------- + -------- +
| SJones | 688 |
| SJones | 689 |
+ ---------- + -------- +
2 hàng in set (0.02 giây)

Bây giờ, tôi có thể viết lại truy vấn thay thế các giá trị ipa_id cho tên người dùng trong mệnh đề where. Đối với tôi, điều này tương đương với truy vấn ban đầu. MySQL thấy nó khác đi. Nếu tôi làm điều này, trình tối ưu hóa sử dụng chỉ mục trên bảng lớn.

mysql> giải thích
CHỌN master_final.PayorCode, sum (master_final.Mbrct) AS MbrCt
TỪ master_final
INNER JOIN ipa ON ipa.ipa_code = master_final.IPA
INNER JOIN assign_ipa ON ipa.ipa_id = assign_ipa.ipa_id
* WHERE assign_ipa.ipa_id trong ('688', '689') *
GROUP BY master_final.PayorCode, master_final.ipa \ G;
* ** * ** * ** * ** * 1.hàng * ** * ** * ** * ** *
id: 1
select_type: SIMPLE
bảng: ipa
loại: phạm vi
possible_keys: PRIMARY, ipa_code
khóa: PRIMARY
key_len: 4
ref: NULL
hàng: 2
Thêm: Sử dụng vị trí; Sử dụng tạm thời; Sử dụng filesort
* ** * ** * ** * ** * 2. hàng * ** * ** * ** * ** *
id: 1
select_type: SIMPLE
bảng: assign_ipa
loại: ref
possible_keys: ipa_id
chính: ipa_id
key_len: 4
ref: wc_test.ipa.ipa_id
hàng: 37
tắm: Sử dụng nơi
* ** * ** * ** * ** * 3. hàng * ** * ** * ** * ** *
id: 1
select_type: SIMPLE
bảng: master_final
loại: ref
possible_keys: idx_IPA
khóa: idx_IPA
key_len: 5
ref: wc_test.ipa.ipa_code
hàng:
tắm: Sử dụng nơi
3 hàng trong set (0.00 sec)

Điều duy nhất tôi đã thay đổi là một mệnh đề where mà thậm chí không trực tiếp đánh vào cái bàn lớn. Tuy nhiên, trình tối ưu hóa sử dụng chỉ mục 'idx_IPA' trên bảng lớn và quét toàn bộ bảng không còn được sử dụng nữa. Truy vấn khi viết lại như thế này rất nhanh.

OK, đó là rất nhiều nền tảng. Bây giờ câu hỏi của tôi. Tại sao mệnh đề where lại quan trọng với trình tối ưu hóa? Hoặc mệnh đề where sẽ trả về cùng một tập kết quả từ bảng nhỏ hơn, nhưng tôi nhận được các kết quả khác nhau đáng kể tùy thuộc vào kết quả tôi sử dụng. Rõ ràng, tôi muốn sử dụng mệnh đề where chứa tên người dùng thay vì cố gắng chuyển tất cả các ID liên quan đến truy vấn. Như được viết mặc dù, điều này là không thể?

  1. Ai đó có thể giải thích lý do tại sao điều này xảy ra?
  2. Tôi làm cách nào để viết lại truy vấn của mình để tránh quét toàn bộ bảng?

Cảm ơn bạn đã gắn bó với tôi. Tôi biết đó là một câu hỏi rất dài.

+0

Tôi đã đọc một bài viết của một trong những nhà phát triển MySQL (một thời gian trước) rằng trình tối ưu hóa vẫn là một công việc đang được tiến hành - và sau đó chúng được Oracle hấp thụ. Bạn đã thử sử dụng "gợi ý" hoặc có thể di chuyển "assign_ipa.username = 'SJones'" vào JOIN? –

Trả lời

4

Không hoàn toàn chắc chắn nếu tôi đúng, nhưng tôi nghĩ những điều sau đây đang xảy ra ở đây. Điều này:

WHERE assign_ipa.username = 'SJones' 

có thể tạo bảng tạm thời vì nó yêu cầu quét toàn bộ bảng. Các bảng tạm thời không có chỉ mục, và chúng có xu hướng làm chậm mọi thứ xuống rất nhiều.

Trường hợp thứ hai

INNER JOIN ipa ON ipa.ipa_code = master_final.IPA 
INNER JOIN assign_ipa ON ipa.ipa_id = assign_ipa.ipa_id 
WHERE assign_ipa.ipa_id in ('688','689') 

mặt khác cho phép tham gia các chỉ số, mà là nhanh chóng. Ngoài ra, nó có thể được chuyển thành

SELECT .... FROM master_final WHERE IDA IN (688, 689) ... 

và tôi nghĩ rằng MySQL cũng đang làm điều đó.

Tạo chỉ mục trên assign_ipa.username có thể hữu ích.

Sửa

tôi xem xét lại vấn đề này và bây giờ có một lời giải thích khác nhau.

Lý do tất nhiên là chỉ mục bị thiếu. Điều này có nghĩa là MySQL không có đầu mối lớn như thế nào kết quả của truy vấn assign_ipa sẽ là (MySQL không lưu trữ đếm), do đó, nó bắt đầu với các tham gia đầu tiên, nơi nó có thể chuyển tiếp trên các phím.

Đó là những gì hàng 2 và 3 của nhật ký giải thích cho chúng tôi biết.

Và sau đó, nó cố gắng để lọc kết quả bằng assign_ipa.username, mà không có chìa khóa, như đã nói ở hàng 1.

Ngay sau khi có một chỉ số, nó lọc assign_ipa đầu tiên, và sau đó tham gia , sử dụng các chỉ mục theo.

+0

Tôi đã thêm một chỉ mục trên assign_ipa.username và chắc chắn đủ, điều đó đã khắc phục được sự cố. Rực rỡ! Tôi sẽ không bao giờ nghĩ về điều đó. Cảm ơn sự giúp đỡ của bạn! – craigm

+0

+1 nhưng xin lỗi tôi không biết gì: tại sao điều đó tạo ra một bảng tạm thời? Để lưu trữ các ID từ assign_ipa được phát hiện trong quá trình quét? Điều đó thực sự được thực hiện vào một bảng tạm thời cho một số lượng nhỏ các trận đấu, và đây có phải là điều tôi cần phải lo lắng về các DB khác (Oracle, SQL) không? Cảm ơn! – Rup

+0

Tôi không biết Oracle và MS SQL là tốt, nhưng tôi nghĩ rằng họ là tốt hơn nhiều trong việc sử dụng kích thước bảng để ước tính chi phí truy vấn. –

2

Đây có lẽ không phải là một câu trả lời trực tiếp câu hỏi của bạn, nhưng đây là một vài điều mà bạn có thể làm:

  1. Run ANALYZE_TABLE ... nó sẽ cập nhật bảng thống kê trong đó có một ảnh hưởng lớn đến những gì ưu sẽ quyết định làm.

  2. Nếu bạn vẫn cho rằng tham gia không theo thứ tự mong muốn (điều này xảy ra trong trường hợp của bạn và do đó trình tối ưu hóa không sử dụng các chỉ mục như bạn mong đợi), bạn có thể sử dụng STRAIGHT_JOIN ... từ here: "STRAIGHT_JOIN buộc trình tối ưu hóa tham gia các bảng theo thứ tự mà chúng được liệt kê trong mệnh đề FROM. Bạn có thể sử dụng điều này để tăng tốc truy vấn nếu trình tối ưu hóa tham gia các bảng theo thứ tự không tối ưu"

  3. , đặt "nơi một phần" ngay vào tham gia đôi khi tạo sự khác biệt và tăng tốc mọi thứ. Ví dụ, bạn có thể viết:

...t1 INNER JOIN t2 ON t1.k1 = t2.k2 AND t2.k2=something...

thay vì

...t1 INNER JOIN t2 ON t1.k1 = t2.k2 .... WHERE t2.k2=something...

Vì vậy, đây chắc chắn không phải là một lời giải thích về lý do tại sao bạn có hành vi nhưng chỉ cần vài gợi ý. Trình tối ưu hóa truy vấn là một con thú lạ, nhưng may mắn thay có lệnh GIẢI THÍCH có thể giúp bạn lừa nó hoạt động theo cách bạn muốn.

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