2011-11-21 45 views
11

Tôi đã nghiên cứu này, nhưng tôi vẫn không thể giải thích tại sao:Tại sao mySQL truy vấn, trái tham gia 'đáng kể' nhanh hơn so với bên trong của tôi tham gia

SELECT cl.`cl_boolean`, l.`l_name` 
FROM `card_legality` cl 
INNER JOIN `legality` l ON l.`legality_id` = cl.`legality_id` 
WHERE cl.`card_id` = 23155 

là chậm hơn so với cách đáng kể:

SELECT cl.`cl_boolean`, l.`l_name` 
FROM `card_legality` cl 
LEFT JOIN `legality` l ON l.`legality_id` = cl.`legality_id` 
WHERE cl.`card_id` = 23155 

115ms Vs 478 mili giây. Cả hai đều sử dụng InnoDB và có những mối quan hệ được xác định. 'Card_legality' chứa khoảng 200 nghìn hàng, trong khi bảng 'hợp pháp' chứa 11 hàng. Dưới đây là cấu trúc cho mỗi:

CREATE TABLE `card_legality` (
    `card_id` varchar(8) NOT NULL DEFAULT '', 
    `legality_id` int(3) NOT NULL, 
    `cl_boolean` tinyint(1) NOT NULL, 
    PRIMARY KEY (`card_id`,`legality_id`), 
    KEY `legality_id` (`legality_id`), 
    CONSTRAINT `card_legality_ibfk_2` FOREIGN KEY (`legality_id`) REFERENCES `legality` (`legality_id`), 
    CONSTRAINT `card_legality_ibfk_1` FOREIGN KEY (`card_id`) REFERENCES `card` (`card_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

Và:

CREATE TABLE `legality` (
    `legality_id` int(3) NOT NULL AUTO_INCREMENT, 
    `l_name` varchar(16) NOT NULL DEFAULT '', 
    PRIMARY KEY (`legality_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1; 

tôi chỉ đơn giản có thể sử dụng LEFT-JOIN, nhưng nó dường như không hoàn toàn đúng ... bất kỳ suy nghĩ, xin vui lòng?

CẬP NHẬT: Theo yêu cầu, tôi đã bao gồm kết quả giải thích cho từng câu hỏi. Tôi đã chạy nó trước đây, nhưng tôi không giả vờ để có một sự hiểu biết thấu đáo về nó ..

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE cl ALL PRIMARY NULL NULL NULL 199747 Using where 
1 SIMPLE l eq_ref PRIMARY PRIMARY 4 hexproof.co.uk.cl.legality_id 1 

AND, bên tham gia:

id select_type table type possible_keys key key_len   ref       rows Extra 
1 SIMPLE l ALL PRIMARY NULL NULL NULL 11 
1 SIMPLE cl ref PRIMARY,legality_id legality_id 4 hexproof.co.uk.l.legality_id 33799 Using where 
+0

Bằng cách này 'card_id' là VARCHAR vì tôi không có lựa chọn, tôi sẽ không chấp nhận điều đó bình thường. – Ben

Trả lời

9

Đó là do varchar trên card_id. MySQL không thể sử dụng chỉ mục trên card_id như card_id như được mô tả ở đây mysql type conversion. Phần quan trọng là

Để so sánh cột có số, MySQL không thể sử dụng chỉ mục trên cột để tra cứu giá trị nhanh chóng. Nếu str_col là cột được lập chỉ mục , chỉ mục không thể được sử dụng khi thực hiện tra cứu trong tuyên bố sau:

SELECT * FROM tbl_name WHERE str_col = 1;

Lý do cho điều này là có nhiều chuỗi khác nhau có thể chuyển đổi thành giá trị 1, chẳng hạn như '1', '1' hoặc '1a'.

Nếu bạn thay đổi các truy vấn của bạn để

SELECT cl.`cl_boolean`, l.`l_name` 
FROM `card_legality` cl 
INNER JOIN `legality` l ON l.`legality_id` = cl.`legality_id` 
WHERE cl.`card_id` = '23155' 

SELECT cl.`cl_boolean`, l.`l_name` 
FROM `card_legality` cl 
LEFT JOIN `legality` l ON l.`legality_id` = cl.`legality_id` 
WHERE cl.`card_id` = '23155' 

Bạn sẽ thấy một cải tiến lớn về tốc độ và cũng có thể xem giải thích một khác nhau.

Dưới đây là một thử nghiệm tương tự (nhưng dễ dàng hơn) để hiển thị này:

> desc id_test; 
+-------+------------+------+-----+---------+-------+ 
| Field | Type  | Null | Key | Default | Extra | 
+-------+------------+------+-----+---------+-------+ 
| id | varchar(8) | NO | PRI | NULL |  | 
+-------+------------+------+-----+---------+-------+ 
1 row in set (0.17 sec) 

> select * from id_test; 
+----+ 
| id | 
+----+ 
| 1 | 
| 2 | 
| 3 | 
| 4 | 
| 5 | 
| 6 | 
| 7 | 
| 8 | 
| 9 | 
+----+ 
9 rows in set (0.00 sec) 

> explain select * from id_test where id = 1; 
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra     | 
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ 
| 1 | SIMPLE  | id_test | index | PRIMARY  | PRIMARY | 10  | NULL | 9 | Using where; Using index | 
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ 
1 row in set (0.00 sec) 


> explain select * from id_test where id = '1'; 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ 
| 1 | SIMPLE  | id_test | const | PRIMARY  | PRIMARY | 10  | const | 1 | Using index | 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ 
1 row in set (0.00 sec) 

Trong trường hợp đầu tiên có Using where; Using index và thứ hai là Using index. Ngoài ra, ref là NULL hoặc CONST. Không cần phải nói, thứ hai là tốt hơn.

+0

aha! Rực rỡ! Bạn là chính xác, tìm kiếm trên 'card_id' như một INT, trong khi trên thực tế nó là một VARCHAR làm cho các truy vấn này gần 600x chậm hơn so với họ nên có được! Cảm ơn bạn Andreas :) – Ben

+0

Yeah. Thông tin rất tốt ở đây nhờ Andreas. – stefgosselin

+0

Tôi đã thêm một liên kết nơi điều này được giải thích. Đó là đọc tốt. –

3

Tôi muốn thử EXPLAIN trên cả những truy vấn. Chỉ cần tiền tố mỗi SELECT với EXPLAIN và chạy chúng. Nó cung cấp thông tin thực sự hữu ích về cách mySQL tối ưu hóa và thực hiện các truy vấn.

+3

Dựa trên chủ đề ở bàn tay tôi muốn nói rằng có một cơ hội khá lớn mà OP biết được cách sử dụng "GIẢI THÍCH". Dù bằng cách nào thì loại thông tin này sẽ được đưa vào nhận xét chứ không phải trong câu trả lời vì bạn không cố gắng trả lời câu hỏi của mình. – Naatan

+0

Xin chào L2G, cảm ơn nhận xét của bạn. Tôi sẽ kiên trì với hàm ** GIẢI THÍCH ** và xem những gì tôi có thể tìm thấy. Hướng dẫn mysql có phần thiếu (hoặc ít nhất là với tôi). Nhiều Cảm ơn – Ben

0

Tôi khá chắc chắn rằng MySql có tối ưu hóa tốt hơn cho việc tham gia trái - không có bằng chứng để sao lưu điều này vào lúc này.

ETA: Một vòng trinh sát nhanh chóng và tôi không thể tìm thấy bất cứ điều gì cụ thể để duy trì quan điểm của tôi như vậy .....

+0

Cảm ơn K.Bob, tôi đã đọc tương tự, nhưng giống như chính bạn; không tìm thấy bằng chứng. – Ben

2

L2G có nó khá nhiều tóm tắt, mặc dù tôi nghi ngờ nó có thể là do các varchar loại được sử dụng cho card_id.

Tôi thực sự đã in ra this informative page để đo điểm chuẩn/định dạng nhanh. Đây là một kỹ thuật định hình người nghèo nhanh chóng:

Time a SQL on MySQL 
Enable Profiling 
mysql> SET PROFILING = 1 
... 
RUN your SQLs 
... 
mysql> SHOW PROFILES; 

+----------+------------+-----------------------+ 
| Query_ID | Duration | Query     | 
+----------+------------+-----------------------+ 
|  1 | 0.00014600 | SELECT DATABASE()  | 
|  2 | 0.00024250 | select user from user | 
+----------+------------+-----------------------+ 
mysql> SHOW PROFILE for QUERY 2; 

+--------------------------------+----------+ 
| Status       | Duration | 
+--------------------------------+----------+ 
| starting      | 0.000034 | 
| checking query cache for query | 0.000033 | 
| checking permissions   | 0.000006 | 
| Opening tables     | 0.000011 | 
| init       | 0.000013 | 
| optimizing      | 0.000004 | 
| executing      | 0.000011 | 
| end       | 0.000004 | 
| query end      | 0.000002 | 
| freeing items     | 0.000026 | 
| logging slow query    | 0.000002 | 
| cleaning up     | 0.000003 | 
+--------------------------------+----------+ 

Chúc may mắn, xin vui lòng gửi phát hiện của bạn!

+0

Đây là thông tin thực sự hữu ích stefgosselin, cảm ơn bạn. Tiên lượng của bạn thực sự đúng, nhưng lời giải thích của Andreas đã giải thích nó. Cảm ơn sự giúp đỡ của bạn :) – Ben

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