MySQL在JOIN查询中没有使用索引

时间:2017-02-05 22:34:53

标签: mysql sql

我有一个500000+行的主表。

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:

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 o eq_ref PRIMARY,idx_orgao_id PRIMARY 4 concursos.f.orgao_id 1

1 SIMPLE c eq_ref PRIMARY,idx_cargo_id PRIMARY 4 concursos.f.cargo_id 1

1 SIMPLE a 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为什么不使用索引?

任何帮助表示感谢。

2 个答案:

答案 0 :(得分:0)

你有几个错误的方法说。首先,您的查询没有order by子句。不保证查询在多次执行时以相同的顺序返回结果(尽管在实际查询中,调试这样的问题可能非常困难)。

因此,您应该在order by的主键上添加esc_questions,并且必须添加任何辅助键。

其次,400020的偏移量相当大。在找到第400,021行之前,MySQL将生成400,020行并丢弃它们。

我的建议是找到" id"在排序中使用,然后包含where子句:

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

这可能不会(或可能)第一次加速负载,但它应该加速后续负载。

答案 1 :(得分:0)

我自己找到了解决方案。我需要在JOIN查询中避免使用LIMIT offset。 为了做到这一点,我需要做一些准备: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指出我正确的方向。