다른 테이블에 대해 조인 할 때 인덱스를 사용하지 않는 MySQL


11

두 개의 테이블이 있는데 첫 번째 테이블에는 CMS 내의 모든 기사 / 블로그 게시물이 포함되어 있습니다. 이 기사 중 일부는 잡지에 나타날 수도 있으며,이 경우 잡지 관련 정보가 포함 된 다른 테이블과 외래 키 관계가 있습니다.

다음은 필수적이지 않은 일부 행이 제거 된이 두 테이블에 대한 테이블 작성 구문의 단순화 된 버전입니다.

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에는 총 250,000 개의 기사가 포함되어 있으며이 문제를 로컬로 복제하려는 경우 샘플 데이터베이스로 테스트 데이터베이스를 채우는 데 사용할 수 있는 간단한 Python 스크립트 를 작성했습니다 .

이 표 중 하나를 선택하면 MySQL은 적절한 색인을 선택하거나 기사를 빠르게 검색하는 데 아무런 문제가 없습니다. 그러나 두 테이블이 다음과 같은 간단한 쿼리로 함께 결합 된 경우 :

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이 적절한 쿼리 및 성능 저하를 선택하지 못했습니다. 관련 Explain 확장 (실행 시간이 1 초 이상임)은 다음과 같습니다.

+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| 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                     |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
  • 편집 9 월 30 일 : WHERE이 쿼리 에서 절을 제거 할 수 있지만 EXPLAIN여전히 동일하게 보이고 쿼리 속도가 여전히 느립니다.

잠재적 인 해결책 중 하나는 인덱스를 강제하는 것입니다. 동일한 쿼리 FORCE INDEX (base_articel_date_published)를 실행하면 약 1.6 밀리 초 내에 실행되는 쿼리가 생성됩니다.

+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| 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    |             |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+

여러 가지 이유로 피할 수 있다면이 쿼리에 대해 인덱스를 강제하지 않아도됩니다. 특히이 기본 쿼리는 다양한 방법으로 필터링 / 수정 (예 :에 의한 필터링 issue_slug) 한 후 base_article_date_published더 이상 최상의 인덱스가 아닐 수 있습니다.

누구든지이 쿼리의 성능을 개선하기위한 전략을 제안 할 수 있습니까?


"is_published"열은 두 개 또는 세 개의 값을 유지하는 경우는 정말 인덱스 키를 드롭 수 base_article_is_published( is_published그것이 부울 타입이다 .. .. 나에게 보이는)
레이몬드 Nijland

답 편집
레이몬드 Nijland

답변:


5

이것에 대해서는 데이터가 이미 올바른 정렬이기 때문에 "임시 사용; 파일 정렬 사용"이 필요하지 않습니다.

MySQL에 "임시 사용; 파일 정렬 사용"이 필요한 이유를 알아야합니다.

필요성 제거에 대한 설명은 두 번째 sqlfriddle을 참조하십시오.

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

참조 http://sqlfiddle.com/#!2/302710/2를

꽤 잘 작동합니다. 국가 / 도시 테이블에 대해서도이 시간이 필요했습니다. 예제 데이터가있는 데모를 참조하십시오 http://sqlfiddle.com/#!2/b34870/41

base_article.is_published = 1이 INNER JOIN 전달 테이블이 아래 답변의 쿼리와 같이 더 나은 성능을 제공 할 수 있다고 설명 한 것처럼 항상 1 개의 레코드를 반환하면이 답변을 분석 할 수도 있습니다.

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


인명 구조 답변! 나는 JOIN단지 사용 하고 있었지만 MySQL은 인덱스를 선택하지 않았다. 대단히 감사합니다 Raymond
Maximus

4

쿼리를 리플렉터

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;

또는

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;

색인 수정

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

시도 해봐 !!!


리 팩터 : LIMIT 30하위 쿼리에 있기 때문에 두려워 합니다 .30 행이 모두 mag_articles테이블 에있는 것은 아닙니다 . LIMIT외부 쿼리로을 이동하면 성능이 원본과 동일합니다. 인덱스 수정 : MySQL은 해당 인덱스도 사용하지 않습니다. WHERE원래 쿼리에서 절을 제거해도 차이가없는 것 같습니다.
Joshmaker

두 번째 리 팩터 방법은 엄청나게 훌륭하게 작동했으며 쿼리 시간이 8 초에서 0.3 초로 크게 단축되었습니다 ... 감사합니다!
andreszs
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.