为什么 MySQL 不在单个列上使用索引进行连接(没有 where 或 group by)?

Why MySQL not using index for join on single column (no where or group by)?

这是我的查询:

mysql> explain
    select * from
        (select * from ratings where project_id=1) as r
        left join article_name_id as a
            force index for join (idx_article_name)
            using(article_name);

这是 EXPLAIN 的输出。为什么它不使用索引进行连接?

+----+-------------+------------+------+------------------+-------------+---------+------+----------+-------------+
| id | select_type | table      | type | possible_keys    | key         | key_len | ref  | rows     | Extra       |
+----+-------------+------------+------+------------------+-------------+---------+------+----------+-------------+
|  1 | PRIMARY     | <derived2> | ALL  | NULL             | NULL        | NULL    | NULL |     1725 |             |
|  1 | PRIMARY     | a          | ALL  | idx_article_name | NULL        | NULL    | NULL | 20441326 |             |
|  2 | DERIVED     | ratings    | ref  | idx_project      | idx_project | 5       |      |     1724 | Using where |
+----+-------------+------------+------+------------------+-------------+---------+------+----------+-------------+

编辑: 这是根据目前的建议更新的 query/explain。 idx_article_name_idarticle_name_id (article_name, article_id) 上的索引。

mysql> explain
    select r.*, a.article_id from
        ratings as r
        left join article_name_id as a
            force index for join (idx_article_name_id)
            using (article_name)
        where project_id=1;

+----+-------------+-------+------+---------------------+-------------+---------+-------+----------+-------------+
| id | select_type | table | type | possible_keys       | key         | key_len | ref   | rows     | Extra       |
+----+-------------+-------+------+---------------------+-------------+---------+-------+----------+-------------+
|  1 | SIMPLE      | r     | ref  | idx_project         | idx_project | 5       | const |     1724 | Using where |
|  1 | SIMPLE      | a     | ALL  | idx_article_name_id | NULL        | NULL    | NULL  | 20441326 |             |
+----+-------------+-------+------+---------------------+-------------+---------+-------+----------+-------------+

这是架构

CREATE TABLE `article_name_id` (
  `row_id` int(11) NOT NULL AUTO_INCREMENT,
  `article_name` varchar(256) DEFAULT NULL,
  `article_id` int(11) DEFAULT NULL,
  `from_ts` datetime DEFAULT NULL,
  `to_ts` datetime DEFAULT NULL,
  PRIMARY KEY (`row_id`),
  KEY `idx_article_name` (`article_name`(191)),
  KEY `idx_article_name_id` (`article_name`(191),`article_id`)
) ENGINE=InnoDB AUTO_INCREMENT=20268652 DEFAULT CHARSET=utf8mb4

最可能的解释是优化器估计完整 table 扫描的成本比使用索引的成本

FORCE 关键字实际上并不强制 优化器使用索引。它只告诉优化器完整 table 扫描的成本非常昂贵。

假设指定的索引不是覆盖索引,SELECT列表中的*意味着MySQL将有访问基础 table 中的页面以获取 所有 列的值。优化器可能正在估计将检索的行数占 table 中行的很大百分比。只有当查询检索行的一小部分时,使用索引的成本才会更低。否则,全面扫描会更有效率。

我怀疑派生的 table 对计划有影响,MySQL 不知道派生的 table 的 article_name 列中值的分布.

如果您试图提高性能,添加索引提示可能不是正确的解决方案。

这是为什么前缀索引 (article_name(191)) 实际上没用的一个例子。

要么将 article_name 的定义缩短为 191,要么升级到 5.7,以便您可以索引完整的字符串。

这是Rick's RoTs中的提示之一。