php - 准备好的语句不使用预期的索引
问题描述
我有一个非常大的 IOT 样本表,我正在尝试对其运行相对简单的查询。使用 MySql CLI 正常运行查询会在 ~0.07 秒内返回结果。如果我首先通过 PDO 或通过运行 SQL PREPARE语句准备查询,则请求需要一分钟。
我启用了优化器跟踪功能,看起来在准备语句时,MySql 忽略了它应该使用的索引并对整个表进行文件排序。如果我做错了什么或者这看起来像一个 MySql 错误,我想要任何见解。
该表本身包含超过 1 亿个样本,其中至少有 30 万个与此处查询的设备相关联。我用 MySql 8.0.23 运行了这些测试,但是当我升级到 8.0.25 时,问题仍然存在。
表定义(省略了一些数据行)
Create Table: CREATE TABLE `samples` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`organization_id` int unsigned NOT NULL,
`device_id` int unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`raw_reading` int DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `samples_organization_id_foreign` (`organization_id`),
KEY `samples_reverse_device_id_created_at_organization_id_index` (`device_id`,`created_at` DESC,`organization_id`),
CONSTRAINT `samples_device_id_foreign` FOREIGN KEY (`device_id`) REFERENCES `devices` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT `samples_organization_id_foreign` FOREIGN KEY (`organization_id`) REFERENCES `organizations` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=188315314 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
在 < 1 秒内运行的 Sql
select *
from `samples`
where `samples`.`device_id` = 5852
and `samples`.`device_id` is not null
and `id` != 188315308
order by `created_at` desc
limit 1;
在一分钟内运行的 Sql
prepare test_prep from 'select * from `samples` where `samples`.`device_id` = ? and `samples`.`device_id` is not null and `id` != ? order by `created_at` desc limit 1';
set @a = 5852;
set @b = 188315308;
execute test_prep using @a, @b;
可以在我的 gist中找到未准备好的 SQL 的跟踪,但相关部分是
{
"reconsidering_access_paths_for_index_ordering": {
"clause": "ORDER BY",
"steps": [
],
"index_order_summary": {
"table": "`samples`",
"index_provides_order": true,
"order_direction": "asc",
"index": "samples_reverse_device_id_created_at_organization_id_index",
"plan_changed": false
}
}
},
可以在我的其他要点中找到准备好的查询的跟踪,但相关部分是
{
"reconsidering_access_paths_for_index_ordering": {
"clause": "ORDER BY",
"steps": [
],
"index_order_summary": {
"table": "`samples`",
"index_provides_order": false,
"order_direction": "undefined",
"index": "samples_reverse_device_id_created_at_organization_id_index",
"plan_changed": false
}
}
},
解决方案
摆脱这个,因为= 5852
保证它是错误的:
and `samples`.`device_id` is not null
那么你的索引,或者这个索引,应该可以正常工作。
INDEX(device_id, created_at, id)
不要使用@variables;优化器似乎不看它们包含的值。也就是说,而不是
set @a = 5852;
set @b = 188315308;
execute test_prep using @a, @b;
简单地做
execute test_prep using 5852, 188315308;
考虑在 bugs.mysql.com 上写一份错误报告
我怀疑"order_direction": "undefined"
是问题的一部分。
推荐阅读
- c# - 从 .NET Core 项目引用 .net Standard 程序集会导致引用错误
- java - 从文件中读取给出 java.io.StreamCorruptedException: invalid stream header: 73720027
- powershell - PowerShell 生成一个后台进程以保持活动状态
- c# - 在类中定义公共枚举有什么好处?
- reactjs - Axios 不向服务器端控制器发送标头
- python - 在 pytest 中设置动态文件夹和报告名称
- javascript - 在 Pentaho 中使用 JavaScript 循环一个 json 数组对象
- node.js - 需要一种将 response.body 数据从 express (server.js) 发送到 React (client.js) 的方法
- javascript - 将秒转换为年、月、周、日、小时、分钟和秒
- amazon-web-services - 已停止(CannotCreateContainerError:来自 da 的错误响应)