我希望将我的 MySQL 数据库从 5.7.33 升级到 8.0.28。我将它与 Laravel 应用程序一起使用,该应用程序使用使用存在子查询的
whereHas
方法生成查询。然而,我注意到这两个版本之间的性能大幅下降 - 从约 5 毫秒到 4 秒。
以下是疑问:
select * from `categories` where exists (select * from `jobs` inner join `category_job` on `jobs`.`id` = `category_job`.`job_id` where `categories`.`id` = `category_job`.`category_id`) order by `name` asc
如果我完全删除存在的子查询,查询会再次恢复到合理的速度 - 大约 5 毫秒,那么有趣的是。
select * from `categories` order by `name` asc
我想了解的是为什么两个版本之间的性能变化如此之大 - 如果我的 SQL 中存在可以改进的错误(或者可能在框架中进行更改以避免问题) - 或者这个这只是 MySQL 中的性能回归,我必须忍受它。
我知道第一个查询需要更多工作,并且存在子查询会变慢,但我不明白简单地升级 MySQL 会产生怎样的效果。
当我使用 EXPLAINS 运行查询时,我在 5.7 和 8.0 之间也得到了不同的结果(另请注意,数据库具有相同的索引):
5.7:
id | 选择类型 | 桌子 | 分区 | 类型 | 可能的键 | 键 | key_len | 参考 | 行 | 过滤 | 额外 |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 小学 | 类别 | 全部 | 27 | 100.00 | 使用地点;使用文件排序 | |||||
2 | 依赖子查询 | 类别_工作 | 参考 | category_job_category_id_index,category_job_job_id_index | category_job_category_id_index | 8 | jobsnearme.categories.id | 35253 | 100.00 | ||
2 | 依赖子查询 | 工作 | eq_ref | 小学 | 小学 | 8 | jobsnearme.category_job.job_id | 1 | 100.00 | 使用索引 |
8.0:
id | 选择类型 | 桌子 | 分区 | 类型 | 可能的键 | 键 | key_len | 参考 | 行 | 过滤 | 额外 |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 简单 | 类别 | 空 | 全部 | 小学 | 空 | 空 | 空 | 30 | 100.00 | 使用文件排序 |
1 | 简单 | 空 | eq_ref | 8 | jobsnearme.categories.id | 1 | 100.00 | 空 | |||
2 | 物化 | 工作 | 空 | 索引 | 小学 | jobs_company_id_index | 9 | 空 | 718220 | 100.00 | 使用索引 |
2 | 物化 | 类别_工作 | 空 | 参考 | category_job_category_id_index,category_job_job_id_index | category_job_job_id_index | 8 | jobsnearme.jobs.id | 1 | 100.00 | 空 |
这是 8.0 的
SHOW CREATE TABLE category_job
:
CREATE TABLE `category_job` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`category_id` bigint unsigned NOT NULL,
`job_id` bigint unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `category_job_category_id_index` (`category_id`),
KEY `category_job_job_id_index` (`job_id`),
CONSTRAINT `category_job_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`),
CONSTRAINT `category_job_job_id_foreign` FOREIGN KEY (`job_id`) REFERENCES `jobs` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1070585 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
尝试在optimizer_switch中设置
semijoin=off
并检查EXPLAIN结果
SET optimizer_switch = 'semijoin=off';
多对多表模式中常见的低效率问题。
这样会更快:
CREATE TABLE `category_job` (
`category_id` bigint unsigned NOT NULL,
`job_id` bigint unsigned NOT NULL,
PRIMARY KEY (category_id, job_id),
KEY (job_id, category_id)
) ENGINE=InnoDB