为啥 MySQL 不使用我的索引进行 JOIN?
Posted
技术标签:
【中文标题】为啥 MySQL 不使用我的索引进行 JOIN?【英文标题】:Why is MySQL not using my index for JOIN?为什么 MySQL 不使用我的索引进行 JOIN? 【发布时间】:2020-10-19 16:16:25 【问题描述】:我有一个由 ORM 生成的非常复杂的查询,但是为了这个问题,这里是相关部分:
SELECT
...
FROM
`Broadcast` `t`
LEFT OUTER JOIN
`Site` `site`
ON (`t`.`site_id` = `site`.`id`)
LEFT OUTER JOIN
`Customer` `customer`
ON (`site`.`customer_id` = `customer`.`id`)
LEFT OUTER JOIN
`Domain` `domain`
ON (`customer`.`domain_id` = `domain`.`id`)
...
我的字段定义如下:
>SHOW FIELDS FROM Broadcast;
+---------------------------+--------------------------------------+------+-----+------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------------+--------------------------------------+------+-----+------------------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| site_id | int(10) unsigned | NO | MUL | NULL | |
...
>SHOW FIELDS FROM Site;
+---------------------------+---------------------+------+-----+------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------------+---------------------+------+-----+------------------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| customer_id | int(11) | NO | MUL | 0 | |
...
>SHOW FIELDS FROM Customer;
+---------------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------------+------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| domain_id | int(10) unsigned | NO | MUL | 1 | |
...
>SHOW FIELDS FROM Domain;
+---------------------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
...
为什么我们的一些 ID 是 int(11)
而另一些是 int(10) unsigned
只是历史背景问题。重要的是X_id
外键始终与相关的id
列共享一个类型。我们从不尝试将int(11)
与int(10) unsigned
链接。此外,id
字段始终是表的主键。
我们还有所有外键的索引:
>SHOW INDEXES FROM Broadcast;
+-----------+------------+---------------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+---------------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Broadcast | 0 | PRIMARY | 1 | id | A | 139708 | NULL | NULL | | BTREE | | |
| Broadcast | 1 | site_id | 1 | site_id | A | 1060 | NULL | NULL | | BTREE | | |
...
>SHOW INDEXES FROM Site;
+-------+------------+----------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Site | 0 | PRIMARY | 1 | id | A | 1876 | NULL | NULL | | BTREE | | |
| Site | 1 | customer_id | 1 | customer_id | A | 250 | NULL | NULL | | BTREE | | |
...
>SHOW INDEXES FROM Customer;
+----------+------------+--------------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+--------------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Customer | 0 | PRIMARY | 1 | id | A | 292 | NULL | NULL | | BTREE | | |
| Customer | 1 | domain_id | 1 | domain_id | A | 2 | NULL | NULL | | BTREE | | |
...
>SHOW INDEXES FROM Domain;
+--------+------------+-------------------------------------------+--------------+---------------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+-------------------------------------------+--------------+---------------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Domain | 0 | PRIMARY | 1 | id | A | 2 | NULL | NULL | | BTREE | | |
...
这些索引中的每一个都包含一个字段,因此该字段是索引中的第一个序列。
当我在查询中运行 EXPLAIN ...
时,我得到的输出如下所示:
Site 和 Customer 使用 eq_ref
类型连接,但 Domain 使用 ALL
类型连接。为什么 mysql 不使用我的索引?
我可以通过在 Customer 表名后添加 USE INDEX FOR JOIN (domain_id)
来强制 MySQL 使用我的索引,但这会导致一个新问题:
问题只是从Customer->Domain
关系上升到Site->Customer
关系。我可以在 Site 表名称之后使用 USE INDEX FOR JOIN (customer_id)
将问题进一步上移,正如您所料,这只会导致 Site 表与 ALL
连接输入而不是eq_ref
我一辈子都搞不明白 MySQL 拒绝使用我的索引的原因
【问题讨论】:
域是行数很少的表吗?优化器可以决定对这样的表使用任何索引根本不值得 @ysth 是的,域表有 1 行或 2 行,具体取决于该系统运行的环境。那么当行数很少时不使用索引实际上更好吗? 是的。或者至少不值得担心 【参考方案1】:每个 cmets 中的 @ysth:
如果您要加入的表的行数很少,MySQL 将不会使用索引,因为从磁盘读取索引的开销超过了好处
【讨论】:
以上是关于为啥 MySQL 不使用我的索引进行 JOIN?的主要内容,如果未能解决你的问题,请参考以下文章
mysql中多表连接(不使用join )为啥 超过三张表就报错?