MySQL không sử dụng chỉ mục khi tham gia vào bảng khác


11

Tôi có hai bảng, bảng đầu tiên chứa tất cả các bài viết / bài đăng trên blog trong một CMS. Một số bài viết này cũng có thể xuất hiện trên một tạp chí, trong trường hợp đó chúng có mối quan hệ khóa ngoại với một bảng khác có chứa thông tin cụ thể của tạp chí.

Đây là một phiên bản đơn giản của cú pháp tạo bảng cho hai bảng này với một số hàng không cần thiết được loại bỏ:

CREATE TABLE `base_article` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `date_published` datetime DEFAULT NULL,
  `title` varchar(255) NOT NULL,
  `description` text,
  `content` longtext,
  `is_published` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `base_article_date_published` (`date_published`),
  KEY `base_article_is_published` (`is_published`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `mag_article` (
    `basearticle_ptr_id` int(11) NOT NULL,
    `issue_slug` varchar(8) DEFAULT NULL,
    `rubric` varchar(75) DEFAULT NULL,
    PRIMARY KEY (`basearticle_ptr_id`),
    KEY `mag_article_issue_slug` (`issue_slug`),
    CONSTRAINT `basearticle_ptr_id_refs_id` FOREIGN KEY (`basearticle_ptr_id`) REFERENCES `base_article` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CMS chứa tổng cộng khoảng 250.000 bài viết và tôi đã viết một tập lệnh Python đơn giản có thể được sử dụng để điền vào cơ sở dữ liệu thử nghiệm với dữ liệu mẫu nếu họ muốn sao chép vấn đề này cục bộ.

Nếu tôi chọn từ một trong các bảng này, MySQL không có vấn đề gì trong việc chọn một chỉ mục thích hợp hoặc truy xuất các bài viết một cách nhanh chóng. Tuy nhiên, khi hai bảng được nối với nhau trong một truy vấn đơn giản, chẳng hạn như:

SELECT * FROM `base_article` 
INNER JOIN `mag_article` ON (`mag_article`.`basearticle_ptr_id` = `base_article`.`id`)
WHERE is_published = 1
ORDER BY `base_article`.`date_published` DESC
LIMIT 30

MySQL không chọn được một truy vấn thích hợp và hiệu suất giảm mạnh. Dưới đây là phần giải thích có liên quan được kéo dài (thời gian thực hiện trong hơn một giây):

+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| id | select_type |    table     |  type  |           possible_keys           |   key   | key_len |                  ref                   | rows  | filtered |              Extra              |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
|  1 | SIMPLE      | mag_article  | ALL    | PRIMARY                           | NULL    | NULL    | NULL                                   | 23830 | 100.00   | Using temporary; Using filesort |
|  1 | SIMPLE      | base_article | eq_ref | PRIMARY,base_article_is_published | PRIMARY | 4       | my_test.mag_article.basearticle_ptr_id |     1 | 100.00   | Using where                     |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
  • EDIT SEPT 30: Tôi có thể xóa WHEREmệnh đề khỏi truy vấn này, nhưng truy vấn EXPLAINvẫn giống nhau và truy vấn vẫn chậm.

Một giải pháp tiềm năng là buộc một chỉ số. Chạy cùng một truy vấn với FORCE INDEX (base_articel_date_published)kết quả trong một truy vấn thực hiện trong khoảng 1,6 mili giây.

+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| id | select_type |    table     |  type  | possible_keys |             key             | key_len |           ref           | rows | filtered  |    Extra    |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
|  1 | SIMPLE      | base_article | index  | NULL          | base_article_date_published |       9 | NULL                    |   30 | 833396.69 | Using where |
|  1 | SIMPLE      | mag_article  | eq_ref | PRIMARY       | PRIMARY                     |       4 | my_test.base_article.id |    1 | 100.00    |             |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+

Tôi muốn không phải buộc một chỉ mục trên truy vấn này nếu tôi có thể tránh nó, vì một số lý do. Đáng chú ý nhất, truy vấn cơ bản này có thể được lọc / sửa đổi theo nhiều cách khác nhau (chẳng hạn như lọc theo issue_slug) sau đó base_article_date_publishedcó thể không còn là chỉ mục tốt nhất để sử dụng.

Bất cứ ai cũng có thể đề xuất một chiến lược để cải thiện hiệu suất cho truy vấn này?


nếu cột "is_published" chỉ giữ hai hoặc ba giá trị bạn thực sự có thể bỏ chỉ số đó KEY base_article_is_published( is_published) .. đối với tôi đó là loại boolean ..
Raymond Nijland

đã chỉnh sửa câu trả lời
Raymond Nijland

Câu trả lời:


5

Điều gì về điều này sẽ loại bỏ nhu cầu "Sử dụng tạm thời; Sử dụng tệp" vì dữ liệu đã được sắp xếp đúng.

Bạn cần biết mẹo tại sao MySQL cần "Sử dụng tạm thời; Sử dụng tệp" để loại bỏ nhu cầu đó.

Xem sqlfriddle thứ hai để được giải thích về việc loại bỏ nhu cầu

SELECT
      *
    FROM base_article

    STRAIGHT_JOIN 
      mag_article
    ON
      (mag_article.basearticle_ptr_id = base_article.id)

    WHERE
      base_article.is_published = 1

    ORDER BY
      base_article.date_published DESC

xem http://sqlfiddle.com/#!2/302710/2

Hoạt động khá tốt, tôi cũng cần điều này một thời gian trước đây cho các bảng Quốc gia / thành phố xem bản demo ở đây với dữ liệu ví dụ http://sqlfiddle.com/#!2/b34870/41

Đã chỉnh sửa, bạn cũng có thể muốn phân tích câu trả lời này nếu base_article.is_published = 1 luôn trả về 1 bản ghi như giải thích của bạn, bảng phân phối INNER THAM GIA có thể cho hiệu suất tốt hơn như các truy vấn trong câu trả lời bên dưới

/programming/18738483/mysql-slow-query-USE-filesort/18774937#18774937


Câu trả lời cứu mạng! Tôi JOINchỉ sử dụng nhưng MySQL không chọn chỉ mục. Cảm ơn rất nhiều Raymond
Maximus

4

REFACTOR THE QUERY

SELECT * FROM
(SELECT * FROM base_article
WHERE is_published = 1
ORDER BY date_published LIMIT 30) A
INNER JOIN mag_article B
ON A.id = B.basearticle_ptr_id;

hoặc là

SELECT B.*,C.* FROM
(SELECT id FROM base_article
WHERE is_published = 1
ORDER BY date_published LIMIT 30) A
LEFT JOIN base_article ON A.id = B.id
LEFT JOIN mag_article C ON B.id = C.basearticle_ptr_id;

SỬA ĐỔI INDEXES CỦA BẠN

ALTER TABLE base_article DROP INDEX base_article_is_published;
ALTER TABLE base_article ADD INDEX ispub_datepub_index (is_published,date_published);

HÃY THỬ MỘT LẦN !!!


Refactor: Không hoạt động Tôi sợ, vì LIMIT 30nó nằm trong truy vấn con (không phải tất cả 30 hàng đó cũng sẽ nằm trong mag_articlesbảng). Nếu tôi di chuyển LIMITđến truy vấn bên ngoài, hiệu suất sẽ giống như trong bản gốc của tôi. Sửa đổi chỉ mục: MySQL cũng không sử dụng chỉ mục đó. Xóa WHEREmệnh đề khỏi truy vấn ban đầu của tôi dường như không tạo ra sự khác biệt.
Joshmaker

Phương pháp tái cấu trúc thứ hai hoạt động rất tốt, thời gian truy vấn đã giảm đáng kể từ 8 giây xuống còn 0,3 giây trong bảng của tôi ... cảm ơn ngài !!
andreszs
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.