mysql - 为什么 MySQL 不使用我的索引进行 JOIN?
问题描述
我有一个由 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
列共享一个类型。我们从不尝试将 aint(11)
与 a 联系起来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 表名之后添加来强制 MySQL 使用我的索引USE INDEX FOR JOIN (domain_id)
,但这会导致一个新问题:
问题只是从Customer->Domain
关系上升到Site->Customer
关系。我可以在 Site 表名称之后将问题进一步上移USE INDEX FOR JOIN (customer_id)
,正如您所料,这只会导致Site表与ALL
类型而不是eq_ref
我一辈子都想不通为什么 MySQL 拒绝使用我的索引
解决方案
根据评论中的@ysth:
如果您要加入的表的行数很少,MySQL 将不会使用索引,因为从磁盘读取索引的开销超过了好处
推荐阅读
- asp.net - 带有 Razor 语法问题的 ASP.NET 4.6 MVC 输出缓存
- xamarin - 如何检索使用用户 ID 创建的频道?
- python - 根据熊猫中另一列的长度获取子字符串
- java - Firebase,从所有文档中获取一个数组列表字段
- javascript - 使用 Lat/lon Array 为图标设置动画 - Mapbox js
- r - 在同一递归函数中总结递归函数的结果
- python - 将数据从一个表复制到另一个 sqlite3
- ruby - 如何在 Inspec 的 http 块中使用字符串连接?
- javascript - 无法让 populate() 在 Mongoose 中填写数组
- ios - 实现 URLProtocol 但尊重会话配置