2017-02-05 267 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 


相關數據被存儲到另外五桌,非常類似於此:

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,idx_disciplinas_id 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 1116

1個SIMPLEÒeq_ref PRIMARY,idx_orgao_id PRIMARY 4 concursos.f.orgao_id 1個

1簡單的C eq_ref PRIMARY,idx_cargo_id PRIMARY 4 concursos.f.cargo_id 1

1 SIMPLE一個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的偏移量相當大。在找到第400,021行之前,MySQL將生成400,020行並放棄它們。

我的建議是要找到在排序中使用的「ID」,然後包括where條款:

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

這可能不會(或可能)加快負荷的第一次,但應加快後續的負載。

+0

1.即使在PRIMARY鍵上使用ORDER BY也會增加一個Extra:'Using temporary;在EXPLAIN中使用filesort'並且不會加速此查詢。首先是使用ORDER BY。 2.我不能使用大於%的WHERE ID,因爲ID不是順序的。但是我測試過並且真的加快了加載時間。 – ganchclub