2012-02-09 29 views
10

Tôi có một bảng Joomla với hàng ngàn hàng nội dung (khoảng 3 triệu). Tôi đang gặp một chút rắc rối khi viết lại các truy vấn cơ sở dữ liệu càng nhanh càng tốt khi truy vấn các bảng.Truy vấn MySQL phức tạp vẫn sử dụng filesort mặc dù chỉ mục tồn tại

Đây là truy vấn đầy đủ của tôi:

SELECT cc.title AS category, a.id, a.title, a.alias, a.title_alias, a.introtext, a.fulltext, a.sectionid, a.state, a.catid, a.created, a.created_by, a.created_by_alias, a.modified, a.modified_by, a.checked_out, a.checked_out_time, a.publish_up, a.publish_down, a.attribs, a.hits, a.images, a.urls, a.ordering, a.metakey, a.metadesc, a.access, CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(":", a.id, a.alias) ELSE a.id END AS slug, CASE WHEN CHAR_LENGTH(cc.alias) THEN CONCAT_WS(":", cc.id, cc.alias) ELSE cc.id END AS catslug, CHAR_LENGTH(a.`fulltext`) AS readmore, u.name AS author, u.usertype, g.name AS groups, u.email AS author_email 
FROM j15_content AS a 
LEFT JOIN j15_categories AS cc 
ON a.catid = cc.id 
LEFT JOIN j15_users AS u 
ON u.id = a.created_by 
LEFT JOIN j15_groups AS g 
ON a.access = g.id 
WHERE 1 
AND a.access <= 0 
AND a.catid = 108 
AND a.state = 1 
AND (publish_up = '0000-00-00 00:00:00' OR publish_up <= '2012-02-08 00:16:26') 
AND (publish_down = '0000-00-00 00:00:00' OR publish_down >= '2012-02-08 00:16:26') 
ORDER BY a.title, a.created DESC 
LIMIT 0, 10 

Đây là kết quả từ một giải thích:

+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-----------------------------+ 
| id | select_type | table | type | possible_keys           | key  | key_len | ref      | rows | Extra      | 
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-----------------------------+ 
| 1 | SIMPLE  | a  | ref | idx_access,idx_state,idx_catid,idx_access_state_catid | idx_catid | 4  | const      | 3108187 | Using where; Using filesort | 
| 1 | SIMPLE  | cc | const | PRIMARY            | PRIMARY | 4  | const      |  1 |        | 
| 1 | SIMPLE  | u  | eq_ref | PRIMARY            | PRIMARY | 4  | database.a.created_by  |  1 |        | 
| 1 | SIMPLE  | g  | eq_ref | PRIMARY            | PRIMARY | 1  | database.a.access   |  1 |        | 
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-----------------------------+ 

Và để hiển thị những gì chỉ số tồn tại, SHOW INDEX TỪ j15_content:

+-------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table  | Non_unique | Key_name    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+-------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| j15_content |   0 | PRIMARY    |   1 | id   | A   |  3228356 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_section   |   1 | sectionid | A   |   2 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_access    |   1 | access  | A   |   1 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_checkout   |   1 | checked_out | A   |   2 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_state    |   1 | state  | A   |   2 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_catid    |   1 | catid  | A   |   6 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_createdby   |   1 | created_by | A   |   1 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | title     |   1 | title  | A   |  201772 |  4 | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_access_state_catid |   1 | access  | A   |   1 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_access_state_catid |   2 | state  | A   |   2 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_access_state_catid |   3 | catid  | A   |   7 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_title_created  |   1 | title  | A   |  3228356 |  8 | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_title_created  |   2 | created  | A   |  3228356 |  NULL | NULL |  | BTREE  |   | 
+-------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 

Như bạn có thể thấy có một vài mẩu dữ liệu được lấy từ cơ sở dữ liệu. Bây giờ tôi đã thử nghiệm bằng cách đơn giản hóa truy vấn mà vấn đề thực sự nằm trong mệnh đề ORDER BY. Nếu không có lệnh áp dụng các kết quả, truy vấn là khá nhạy, đây là một lời giải thích:

+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-------------+ 
| id | select_type | table | type | possible_keys           | key  | key_len | ref      | rows | Extra  | 
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-------------+ 
| 1 | SIMPLE  | a  | ref | idx_access,idx_state,idx_catid,idx_access_state_catid | idx_catid | 4  | const      | 3108187 | Using where | 
| 1 | SIMPLE  | cc | const | PRIMARY            | PRIMARY | 4  | const      |  1 |    | 
| 1 | SIMPLE  | u  | eq_ref | PRIMARY            | PRIMARY | 4  | database.a.created_by  |  1 |    | 
| 1 | SIMPLE  | g  | eq_ref | PRIMARY            | PRIMARY | 1  | database.a.access   |  1 |    | 
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-------------+ 

Như bạn thấy đó là filesort chết người đó là giết chết các máy chủ. Với nhiều hàng này, tôi đang cố gắng hết sức để tối ưu hóa mọi thứ thông qua các chỉ mục nhưng vẫn có điều gì đó không đúng với điều này. Bất kỳ đầu vào sẽ được đánh giá rất nhiều.

Cố gắng sử dụng FORCE INDEX vô ích:

explain  SELECT cc.title AS category, a.id, a.title, a.alias, a.title_alias, a.introtext, a.fulltext, a.sectionid, a.state, a.catid, a.created, a.created_by, a.created_by_alias, a.modified, a.modified_by, a.checked_out, a.checked_out_time, a.publish_up, a.publish_down, a.attribs, a.hits, a.images, a.urls, a.ordering, a.metakey, a.metadesc, a.access, CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(":", a.id, a.alias) ELSE a.id END AS slug, CASE WHEN CHAR_LENGTH(cc.alias) THEN CONCAT_WS(":", cc.id, cc.alias) ELSE cc.id END AS catslug, CHAR_LENGTH(a.`fulltext`) AS readmore, u.name AS author, u.usertype, g.name AS groups, u.email AS author_email 
    ->  FROM bak_content AS a 
    ->  FORCE INDEX (idx_title_created) 
    ->  LEFT JOIN bak_categories AS cc 
    ->  ON a.catid = cc.id 
    ->  LEFT JOIN bak_users AS u 
    ->  ON u.id = a.created_by 
    ->  LEFT JOIN bak_groups AS g 
    ->  ON a.access = g.id 
    ->  WHERE 1 
    ->  AND a.access <= 0 
    ->  AND a.catid = 108 
    ->  AND a.state = 1 
    ->  AND (publish_up = '0000-00-00 00:00:00' OR publish_up <= '2012-02-08 
    ->  AND (publish_down = '0000-00-00 00:00:00' OR publish_down >= '2012-0 
    ->  ORDER BY a.title, a.created DESC 
    ->  LIMIT 0, 10; 

Tạo:

+----+-------------+-------+--------+---------------+---------+---------+------- 
| id | select_type | table | type | possible_keys | key  | key_len | ref 
+----+-------------+-------+--------+---------------+---------+---------+------- 
| 1 | SIMPLE  | a  | ALL | NULL   | NULL | NULL | NULL 
| 1 | SIMPLE  | cc | const | PRIMARY  | PRIMARY | 4  | const 
| 1 | SIMPLE  | u  | eq_ref | PRIMARY  | PRIMARY | 4  | database 
| 1 | SIMPLE  | g  | eq_ref | PRIMARY  | PRIMARY | 1  | database 
+----+-------------+-------+--------+---------------+---------+---------+------- 
+0

Tôi sẽ thử một trong các chỉ mục này: '(trạng thái, catid, truy cập)' hoặc '(trạng thái, catid, publish_up)' hoặc '(trạng thái, catid, publish_down)' –

+0

Nếu bạn không sử dụng 'LIMIT' nhiều hàng được trả về? –

+0

Bạn cũng có thể thử sử dụng chỉ mục 'idx_title_created'. –

Trả lời

0

Đôi khi MySQL có khó khăn khi tìm các chỉ số thích hợp. Bạn có thể giải quyết điều này bằng cách gợi ý đến chỉ mục thích hợp.

Gợi ý cú pháp: http://dev.mysql.com/doc/refman/4.1/en/index-hints.html

Đảm bảo bạn có chỉ số đúng và điều chỉnh hiệu suất của nó bằng cách thử nghiệm.

Chúc mừng!

+0

Tôi đã cố gắng buộc chỉ mục nhưng điều này không giải quyết được vấn đề. Có vẻ như tôi có thể đã lập chỉ mục các cột sai nhưng tôi không chắc những cột nào nên/không nên nằm trong chỉ mục. Tôi giả định do tính chất của truy vấn một số chỉ số tổng hợp là bắt buộc. – user1199057

0

Bạn có thể thử thay đổi này:

SELECT cc.title AS category, ... 
FROM 
    (SELECT * 
     FROM j15_content AS a 
       USE INDEX (title)    --- with and without the hint 
     WHERE 1 
     AND a.access <= 0 
     AND a.catid = 108 
     AND a.state = 1 
     AND (publish_up = '0000-00-00 00:00:00' 
      OR publish_up <= '2012-02-08 00:16:26') 
     AND (publish_down = '0000-00-00 00:00:00' 
      OR publish_down >= '2012-02-08 00:16:26') 
     ORDER BY a.title, a.created DESC 
     LIMIT 0, 10 
    ) AS a 
    LEFT JOIN j15_categories AS cc 
    ON a.catid = cc.id 
    LEFT JOIN j15_users AS u 
    ON u.id = a.created_by 
    LEFT JOIN j15_groups AS g 
    ON a.access = g.id 

Một chỉ mục trên (catid, state, title) sẽ tốt hơn tôi nghĩ.

+0

Cảm ơn, tôi sẽ thử nó ngay và quay lại với bạn. Điều gì sẽ là tác động của một chỉ mục trên (tiêu đề, catid, id) (theo thứ tự đó) để các kết quả đã được sắp xếp theo tiêu đề.Tôi đã nghĩ đến việc chia nhỏ điều này thành 2 truy vấn, nếu tôi có thể kéo chỉ các id bài viết và sau đó thực hiện truy vấn riêng biệt, sau đó trả về tất cả thông tin bài viết được liên kết bằng cách sử dụng id IN (1, 2, 3, v.v.) – user1199057

+0

trở lại với: "# 1028 - Sắp xếp bị hủy bỏ" không may. – user1199057

0

Có lẽ cố gắng này có thể giúp:

CREATE INDEX idx_catid_title_created ON j15_content (catid,title(8),created); 
DROP INDEX idx_catid ON j15_content; 
5

AFAIK này không thể được giải quyết một cách hợp lý sử dụng một chỉ số, gợi ý hoặc cơ cấu lại các truy vấn riêng của mình.

Lý do điều này là chậm là thực tế là nó yêu cầu một tệp của 2M hàng thực sự mất một thời gian dài. Nếu bạn phóng to theo thứ tự, nó được chỉ định là ORDER BY a.title, a.created DESC. Vấn đề là sự kết hợp của phân loại trên nhiều hơn 1 cột và có một phần DESC. Mysql không hỗ trợ các chỉ số giảm dần (từ khóa DESC được hỗ trợ trong CREATE INDEX statement nhưng chỉ để sử dụng trong tương lai).

Giải pháp được đề xuất là tạo cột bổ sung 'reverse_created' được tự động điền theo cách truy vấn của bạn có thể sử dụng ORDER BY a.title, a.reverse_created. Vì vậy, bạn điền nó với max_time - created_time.Sau đó, tạo chỉ mục trên kết hợp đó và (nếu cần), hãy chỉ định chỉ mục đó làm gợi ý.

Có một vài điều trên blog thực sự tốt về chủ đề này mà giải thích điều này tốt hơn rất nhiều và với các ví dụ:

-Update- Bạn sẽ có thể để thực hiện kiểm tra nhanh về điều này bằng cách xóa phần "DESC" khỏi đơn đặt hàng bằng truy vấn của bạn. Các kết quả sẽ có chức năng sai, nhưng nó nên sử dụng chỉ mục hiện có mà bạn có (hoặc nếu không thì lực lượng sẽ hoạt động).

0

Bạn đã cố gắng tăng các giá trị tmp_table_size và max_heap_table_size:

Có một lời giải thích ngắn here và cũng liên kết đến từng chi tiết của mỗi trong số họ.

Hy vọng điều này sẽ hữu ích!

0

Tôi hy vọng điều này là đúng cú pháp

SELECT 
    cc.title AS category, 
    a.id, a.title, a.alias, a.title_alias, 
    a.introtext, a.fulltext, a.sectionid, 
    a.state, a.catid, a.created, a.created_by, 
    a.created_by_alias, a.modified, a.modified_by, 
    a.checked_out, a.checked_out_time, 
    a.publish_up, a.publish_down, a.attribs, 
    a.hits, a.images, a.urls, a.ordering, a.metakey, 
    a.metadesc, a.access, 
    CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(":", a.id, a.alias) ELSE a.id END AS slug, 
    CASE WHEN CHAR_LENGTH(cc.alias) THEN CONCAT_WS(":", cc.id, cc.alias) ELSE cc.id END AS catslug, CHAR_LENGTH(a.`fulltext`) AS readmore, 
    u.name AS author, u.usertype, g.name AS groups, u.email AS author_email 
FROM 
(
    SELECT aa.* 
    FROM 
    (
     SELECT id FROM 
     FROM j15_content 
     WHERE catid=108 AND state=1 
     AND a.access <= 0 
     AND (publish_up = '0000-00-00 00:00:00' OR publish_up <= '2012-02-08 00:16:26') 
     AND (publish_down = '0000-00-00 00:00:00' OR publish_down >= '2012-02-08 00:16:26') 
     ORDER BY title,created DESC 
     LIMIT 0,10 
    ) needed_keys 
    LEFT JOIN j15_content aa USING (id) 
) a 
LEFT JOIN j15_categories AS cc ON a.catid = cc.id 
LEFT JOIN j15_users AS u ON a.created_by = u.id 
LEFT JOIN j15_groups AS g ON a.access = g.id; 

Bạn sẽ cần một số hỗ trợ cho needed_keys subquery

ALTER TABLE j15_content ADD INDEX subquery_ndx (catid,state,access,title,created); 

cho nó một thử !!!

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