MySQL 在连接另一个表时不使用索引

Jos*_*ker 12 mysql performance index join order-by

我有两个表,第一个表包含 CMS 中的所有文章/博客文章。其中一些文章也可能出现在杂志中,在这种情况下,它们与包含杂志特定信息的另一个表具有外键关系。

这是这两个表的 create table 语法的简化版本,其中删除了一些非必要的行:

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;
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

MySQL 无法选择合适的索引并且性能下降。这是相关的解释扩展(执行时间超过一秒):

+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| 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                     |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
Run Code Online (Sandbox Code Playgroud)
  • 编辑 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    |             |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
Run Code Online (Sandbox Code Playgroud)

如果可以避免的话,我宁愿不必在此查询上强制建立索引,原因有很多。最值得注意的是,这个基本查询可以通过多种方式进行过滤/修改(例如按 过滤issue_slug),之后base_article_date_published可能不再是最好使用的索引。

任何人都可以提出一种提高此查询性能的策略吗?

Ray*_*and 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
Run Code Online (Sandbox Code Playgroud)

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

效果很好,我前段时间也需要这个用于国家/城市表,请参见此处的演示示例数据http://sqlfiddle.com/#!2/b34870/41

编辑过,如果 base_article.is_published = 1 总是返回 1 条记录,就像您的解释一样,您可能还想分析此答案 INNER JOIN 交付的表可能会提供更好的性能,如以下答案中的查询

/sf/ask/1311693841/#18774937