gpt4 book ai didi

mysql - 在对索引列进行排序时,如何避免在具有大表的 MySQL 中选择糟糕的执行计划?

转载 作者:行者123 更新时间:2023-11-30 22:51:48 27 4
gpt4 key购买 nike

我在 MySQL 5.5 上有三个 InnoDB 表:

user_company_assignment (1.7 million rows)
- id (int)
- company_id (int) FKEY (company.id)
- user_id (int) FKEY (user.id)

company (200k rows)
- id (int)
- name (varchar(255)) - Indexed

user (2 million rows)
- id (int)
- name (varchar(255)) - Indexed

连接所有表并按索引 varchar 字段之一排序具有良好的执行计划并且很快(~10 毫秒):

mysql> explain select user_company_assignment.id, company.name
from user_company_assignment
inner join company
on user_company_assignment.company_id = company.id
inner join user
on user_company_assignment.user_id = user.id
order by company.name asc limit 5;
+----+-------------+------------+--------+-----------------------------+-------------+---------+------------------------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+-----------------------------+-------------+---------+------------------------------------------+------+-------------+
| 1 | SIMPLE | company | index | PRIMARY | company_name | 767 | NULL | 1 | Using index |
| 1 | SIMPLE | user_company_assignment | ref | fk_company_id,fk_user_id | fk_company_id | 4 | company.id | 4 | |
| 1 | SIMPLE | user | eq_ref | PRIMARY | PRIMARY | 4 | user_company_assignment.user_id | 1 | Using index |
+----+-------------+------------+--------+-----------------------------+-------------+---------+------------------------------------------+------+-------------+
3 rows in set (0.09 sec)

但是当我连接两个表并按它们的每个索引名称排序时,它会选择一个非常慢的执行计划并且查询需要一分多钟才能完成:

mysql> explain select user_company_assignment.id, company.name
from user_company_assignment
inner join company
on user_company_assignment.company_id = company.id
inner join user
on user_company_assignment.user_id = user.id
order by company.name asc, user.name asc limit 5;
+----+-------------+------------+--------+-----------------------------+-------------+---------+------------------------------------------+--------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+-----------------------------+-------------+---------+------------------------------------------+--------+----------------------------------------------+
| 1 | SIMPLE | company | index | PRIMARY | company_name | 767 | NULL | 180792 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | user_company_assignment | ref | fk_company_id,fk_user_id | fk_company_id | 4 | company.id | 4 | |
| 1 | SIMPLE | user | eq_ref | PRIMARY | PRIMARY | 4 | user_company_assignment.user_id | 1 | |
+----+-------------+------------+--------+-----------------------------+-------------+---------+------------------------------------------+--------+----------------------------------------------+
3 rows in set (0.11 sec)

有没有办法获得将使用这两个表列上的索引的执行计划?我不希望它像仅按一列排序时那样快,但我觉得它应该只慢一点。

最佳答案

你可以用迂回的方式做你想做的事。

不幸的是,在 InnoDB 中无法创建包含来自单独表的列的键。因此,如果您按不同表中的列排序,然后使用 ORDER BY ... LIMIT 将它们切断,则 MySQL 将不得不选择每个匹配的行。这样做之后,它可以对磁盘上的大结果集进行排序,最后选择您想要的前几名。

解决方案是通过将匹配结果集限制为可管理的内容来减少 MySQL 必须操作的行数。

因为您知道最多总共有 5 个结果,所以您可以选择前 5 个公司 ID:

SELECT company.id
FROM company
ORDER BY company.name ASC
LIMIT 5

然后仅使用这些进行 JOIN,最后按公司和用户名排序:

SELECT user_company_assignment.id, top_companies.name
FROM user_company_assignment
INNER JOIN (
SELECT company.name, company.id
FROM company
ORDER BY company.name ASC
LIMIT 5
) top_companies
ON user_company_assignment.id = top_companies.id
INNER JOIN user
ON user_company_assignment.user_id = user.id
ORDER BY top_companies.name ASC, user.name ASC
LIMIT 5

Here's a SQL fiddle demonstrating the query running .我投入了少量数据进行测试。

这将仍然拉入相当数量的行进行文件排序,因为您将加入 5 家公司的所有员工,但它应该将您的查询的运行时间缩短几个数量级。

关于mysql - 在对索引列进行排序时,如何避免在具有大表的 MySQL 中选择糟糕的执行计划?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27971955/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com