2017-02-05 1 views
0

500000+ 개의 행을 가진 메인 테이블을 가지고 있습니다.MySQL은 JOIN 쿼리에서 인덱스를 사용하지 않습니다.

CREATE TABLE `esc_questions`(
    `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `esc_id` INT(11) NOT NULL, 
    `question_text` LONGTEXT COLLATE utf8_unicode_ci NOT NULL, 
    `answer_1` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `answer_2` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `answer_3` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `answer_4` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `answer_5` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `right_answer` VARCHAR(255) COLLATE utf8_unicode_ci NOT NULL, 
    `disciplinas_id` INT(11) UNSIGNED NOT NULL, 
    `assunto_id` INT(11) UNSIGNED NOT NULL, 
    `orgao_id` INT(11) UNSIGNED NOT NULL, 
    `cargo_id` INT(11) UNSIGNED NOT NULL, 
    `ano` INT(11) NOT NULL, 
    `banca_id` INT(11) UNSIGNED NOT NULL, 
    `question_type` TINYINT(4) NOT NULL, 
    `url` TEXT COLLATE utf8_unicode_ci NOT NULL, 
    `created_at` TIMESTAMP NULL DEFAULT NULL, 
    `updated_at` TIMESTAMP NULL DEFAULT NULL, 
    PRIMARY KEY(`id`), 
    KEY `idx_ano`(`ano`) USING BTREE, 
    KEY `idx_question_type`(`question_type`) USING BTREE, 
    KEY `idx_cargo_id`(`cargo_id`) USING BTREE, 
    KEY `idx_orgao_id`(`orgao_id`) USING BTREE, 
    KEY `idx_banca_id`(`banca_id`) USING BTREE, 
    KEY `idx_question_id`(`id`) USING BTREE, 
    KEY `idx_assunto_id`(`assunto_id`) USING BTREE, 
    KEY `idx_disciplinas_id`(`disciplinas_id`) USING BTREE, 
    CONSTRAINT `fk_assunto_id` FOREIGN KEY(`assunto_id`) REFERENCES `esc_assunto`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    CONSTRAINT `fk_banca_id` FOREIGN KEY(`banca_id`) REFERENCES `esc_bancas`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    CONSTRAINT `fk_cargo_id` FOREIGN KEY(`cargo_id`) REFERENCES `esc_cargo`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    CONSTRAINT `fk_disciplinas_id` FOREIGN KEY(`disciplinas_id`) REFERENCES `esc_disciplinas`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    CONSTRAINT `fk_orgao_id` FOREIGN KEY(`orgao_id`) REFERENCES `esc_orgao`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION 
) ENGINE = INNODB AUTO_INCREMENT = 516157 DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci 


관련 데이터는이 매우 유사 5 개 개의 추가 테이블에 저장됩니다

CREATE TABLE `esc_assunto`(
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, 
`name` VARCHAR(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
PRIMARY KEY(`id`), 
KEY `idx_assunto_id`(`id`) USING BTREE, 
KEY `idx_assunto_name`(`name`(30)), 
CONSTRAINT `fk_assunto` FOREIGN KEY(`id`) REFERENCES `esc_questions`(`assunto_id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = INNODB AUTO_INCREMENT = 3618 DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci 


내 웹 사이트에 페이지 매김이있다. 최신 페이지를 얻으려고 할 때 데이터 요청에 걸리는 시간이 증가하고 있습니다. 다음은이 작업을위한 나의 선택이다 :

SELECT 
    f.*, 
    d.name disciplinas, 
    o.name orgao, 
    c.name cargo, 
    b.name banca, 
    a.name assunto 
FROM 
    `esc_questions` f 
INNER JOIN 
    `esc_bancas` b 
ON 
    f.banca_id = b.id 
INNER JOIN 
    `esc_disciplinas` d 
ON 
    f.disciplinas_id = d.id 
INNER JOIN 
    `esc_assunto` a 
ON 
    f.assunto_id = a.id 
INNER JOIN 
    `esc_orgao` o 
ON 
    f.orgao_id = o.id 
INNER JOIN 
    `esc_cargo` c 
ON 
    f.cargo_id = c.id 
LIMIT 400020, 20 

이 쿼리는 Sending Data 단계는 쿼리 프로파일에 나타났다에 시간이 오래 걸립니다.

Sending Data 17.6 s 99.99% 1 17.6 s는 EXPLAIN
다음 보여준다
1 SIMPLE D ALL PRIMARY을 247

1 SIMPLE F REF idx_cargo_id, idx_orgao_id, idx_banca_id, idx_assunto_id, idx_disciplinas_id idx_disciplinas_id 4 concursos.d.id 1,116

을 idx_disciplinas_id eq_ref PRIMARY O 1 SIMPLE, idx_orgao_id PRIMARY 4 concursos.f.orgao_id 1

1 간단한 C eq_ref 차, 4 idx_cargo_id PRIMARY concursos.f.cargo_id 1

간단 eq_ref의 PRIMARY, idx_assunto_id PRIMARY 4 concursos.f.assunto_id 1

1 SIMPLE B의 eq_ref의 PRIMARY, idx_bancas_id PRIMARY 4 concursos.f.banca_id 1


나는이 작업을 빠르게하기 위해 하루 종일 보냈지 만 성공하지 못했습니다.

누가 내 선택 쿼리가 잘못되었거나 MySQL이 인덱스를 사용하지 않는 이유를 말해 줄 수 있습니까?

도움을 주시면 감사하겠습니다.

답변

0

해결책을 직접 찾았습니다. 내 JOIN 쿼리에서 LIMIToffset으로 회피해야합니다. 이렇게하려면 몇 가지 준비가 필요합니다.
1. 필요한 오프셋에 조인하지 않고 메인 테이블에서 ID 만 가져옵니다. 이 쿼리에는 0이 필요했습니다.0856 초

SELECT id FROM `esc_questions` WHERE 1 LIMIT 489980, 20 

2. 쿼리를 만들기 위해 복합 인덱스를 만듭니다. 제 경우에는 다음 색인을 사용합니다 :

... 
KEY `idx_filter_search` (`id`,`disciplinas_id`,`assunto_id`,`orgao_id`,`cargo_id`,`banca_id`) USING BTREE, 
... 

3. 마지막으로 쿼리를 작성하십시오. Query took 0.0040 seconds :

SELECT SQL_NO_CACHE 
     f.*, 
     d.name disciplinas, 
     o.name orgao, 
     c.name cargo, 
     b.name banca, 
     a.name assunto 
    FROM 
     `esc_questions` f FORCE INDEX(idx_filter_search), 
     `esc_disciplinas` d, 
     `esc_assunto` a, 
     `esc_orgao` o, 
     `esc_cargo` c, 
     `esc_bancas` b 
    WHERE 
     f.id IN(
      497442, 
      497444, 
      497445, 
      497447, 
      497449, 
      497450, 
      497452, 
      497453, 
      497454, 
      497456, 
      497458, 
      497459, 
      497461, 
      497462, 
      497464, 
      497465, 
      497467, 
      497468, 
      497470, 
      497471 
     ) AND f.disciplinas_id = d.id AND f.assunto_id = a.id AND f.orgao_id = o.id AND f.cargo_id = c.id AND f.banca_id = b.id 
    ORDER BY 
     id 

EXPLAIN이 쿼리 내 새로 생성 된 인덱스를 사용하고 있음을 말해 것입니다.
1 | SIMPLE | f | range | idx_filter_search | idx_filter_search | 4 | NULL | 20 | Using where

희망이 있으면 도움이 될 것입니다. @GordonLinoff에게 올바른 방향을 알려 주셔서 감사합니다.

0

여러 접근법이 잘못되었습니다. 첫째, 쿼리에는 order by 절이 없습니다. 질의는 다중 실행에서 같은 순서로 결과를 반환한다고 보장하지는 않습니다 (실제로는 쿼리가 그렇지만 그러한 문제를 디버깅하는 것은 정말로 어려울 수 있습니다).

그러므로 order by을 추가해야합니다. 기본 키는 esc_questions이고 보조 키가 필요한 경우라면 가능합니다.

둘째, 오프셋 400020은 다소 큽니다. MySQL은 400,021 행을 찾기 전에 400,020 개의 행을 생성하여 버립니다.

나의 제안 정렬에 사용 된 "ID"를 찾을 수 있습니다 다음 where 조항을 포함한다 :

where ?? > $last_id 
. . . 
order by ?? 
limit 20 

이는 속도를해야하지 않을 수 있습니다 (또는 수) 부하를 처음 속도,하지만를 후속 적재.

+0

1. PRIMARY 키에도 ORDER BY를 사용하면 Extra가 추가됩니다. EXPLAIN에서 filesort를 사용하고 있으며이 쿼리의 속도가 향상되지 않습니다. 첫째로 ORDER BY와 함께했습니다. 2. ID가 순차적이지 않으므로 % 이상의 WHERE id를 사용할 수 없습니다. 그러나 나는로드 타임을 테스트하고 정말로 빨라졌습니다. – ganchclub

관련 문제