【发布时间】:2019-12-12 14:31:48
【问题描述】:
我试图了解我的 InnoDB 表上以下两个查询之间查询时间的巨大差异:
SELECT *
FROM db_telemetry.monitor_data
WHERE monitor_id = 6
AND created_at > '2019/11/14'
AND created_at < '2019/11/29';
4317 行在 37.672 秒内返回
SELECT *
FROM db_telemetry.monitor_data USE INDEX(ix_monitor_data_created_at)
WHERE monitor_id = 6
AND created_at > '2019/11/14'
AND created_at < '2019/11/29';
4317 行在 0.110 秒内返回
根据EXPLAIN,第一个(慢速)查询中的优化器选择monitor_id 作为其索引键。根据我的阅读,这很奇怪,因为 monitor_id 的基数相对较低(见下文)
我的桌子:
SHOW CREATE TABLE monitor_data
CREATE TABLE `monitor_data` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`monitor_id` int(11) NOT NULL DEFAULT '0',
`vbattery` float DEFAULT NULL,
`rssi` float DEFAULT NULL,
`ecio` float DEFAULT NULL,
`tboard` float DEFAULT NULL,
`txbytes` float DEFAULT NULL,
`rxbytes` float DEFAULT NULL,
`satelite_count` float DEFAULT NULL,
`gps_fix` float DEFAULT NULL,
`drive_space_remaining` float DEFAULT NULL,
`other` text,
`daq_reachable` tinyint(1) DEFAULT NULL,
`monitor_reachable` tinyint(1) DEFAULT NULL,
`clock_reset_flag` tinyint(1) DEFAULT NULL,
`site_key` varchar(50) DEFAULT NULL,
`internal_temp` float DEFAULT NULL,
`vin` float DEFAULT NULL,
`webrelay_reachable` tinyint(1) DEFAULT NULL,
`daq_current_time` datetime DEFAULT NULL,
`webrelay_current_time` datetime DEFAULT NULL,
`latitude` float DEFAULT NULL,
`longitude` float DEFAULT NULL,
`speed` float DEFAULT NULL,
PRIMARY KEY (`id`,`monitor_id`),
KEY `monitor_id` (`monitor_id`),
KEY `ix_monitor_data_site_key` (`site_key`),
KEY `ix_monitor_data_created_at` (`created_at`),
CONSTRAINT `monitor_data_ibfk_1` FOREIGN KEY (`monitor_id`) REFERENCES `monitors` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10839466 DEFAULT CHARSET=latin1
它的索引:
SHOW INDEX FROM monitor_data
Table Non_unique Key_name Seq_in_index Column_name Cardinality
------------------------------------------------------------------------------------------------
monitor_data 0 PRIMARY 1 id 11311240
monitor_data 0 PRIMARY 2 monitor_id 11311240
monitor_data 1 monitor_id 1 monitor_id 110
monitor_data 1 ix_monitor_data_site_key 1 site_key 28137
monitor_data 1 ix_monitor_data_created_at 1 created_at 11311240
Sub_part and Packed all NULL
Index_type all BTREE
Collation all 'A'
这是在具有 20GB 通用 SSD 的 AWS RDS t2.small 实例上运行的 MySQL 版本 5.6.40。
如果我只使用monitor_id 条件:
SELECT *
FROM db_telemetry.monitor_data
WHERE monitor_id = 6;
0.078 秒内返回 274324 行
如果我只使用created_at 条件:
SELECT *
FROM db_telemetry.monitor_data
WHERE created_at > '2019/11/14'
AND created_at < '2019/11/29';
0.109 秒内返回 202976 行
所以,问题:
- 为什么优化器默认选择
monitor_id作为索引,我的架构是否可能存在问题,需要USE INDEX()? - 由于两个索引都是孤立的,因此将数据集减少到相似的 #
行为什么使用
monitor_id的多条件查询要慢得多 索引?
注意:我观察到某些较小的日期范围,优化器会翻转以选择 ix_monitor_data_created_at
【问题讨论】:
-
除了格式化查询,我更喜欢 SHOW CREATE TABLE;我只是觉得两者都更容易理解。 - 这是 MyISAM 吗?为什么?
-
@Strawberry,这是 InnoDB,我更新了问题
-
什么版本的mysql?
-
@PeterHe 版本 5.6.40
-
在 mysql 8.0 之前,mysql innodb 不会持久化统计信息。自服务器启动以来,它仅在运行时内置内存统计信息,因此如果您的数据出现偏差,它可能会非常不准确。不准确的统计数据会导致次优的执行计划。在您的情况下,使用索引提示不是问题。但是如果这是您的重要查询并且经常使用,您可以尝试将索引monitor_id 转换为复合索引以包含列create_at。 mysql 应该自动为您的查询使用这个索引
标签: mysql