同一INSERT语句多次EXPLAIN结果不同的原因排查
正在使用EXPLAIN EXTENDED排查一条慢INSERT语句,发现多次执行同一查询的EXPLAIN EXTENDED返回结果不同,核心表现为possible_keys字段值不稳定——有时包含多个索引,有时仅单个,甚至为空,怀疑索引未被正确使用。
当前环境为测试服务器,无其他流量、定时任务及表结构变更。该问题在其他表也存在,生产与测试环境均出现,测试环境更频繁。
执行的INSERT语句
INSERT INTO `catalog_product_entity_varchar` ( `attribute_id`, `store_id`, `value`, `row_id` ) VALUES ( '442', '0', 'https://www.example.com/item/', '46242' ) ON DUPLICATE KEY UPDATE `attribute_id` = VALUES(`attribute_id`), `store_id` = VALUES(`store_id`), `value` = VALUES(`value`), `row_id` = VALUES(`row_id`);
EXPLAIN查询语句
explain extended INSERT INTO `catalog_product_entity_varchar` ( `attribute_id`, `store_id`, `value`, `row_id` ) VALUES ( '442', '0', 'https://www.sexample.com/item/', '46242' ) ON DUPLICATE KEY UPDATE `attribute_id` = VALUES(`attribute_id`), `store_id` = VALUES(`store_id`), `value` = VALUES(`value`), `row_id` = VALUES(`row_id`);
版本信息
aurora_version 2.11.1 innodb_version 5.7.12 protocol_version 10 slave_type_conversions tls_version TLSv1,TLSv1.1,TLSv1.2 version 5.7.12-log version_comment MySQL Community Server (GPL) version_compile_machine x86_64 version_compile_os Linux
表结构
CREATE TABLE `catalog_product_entity_varchar` ( `value_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Value ID', `attribute_id` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Attribute ID', `store_id` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Store ID', `value` varchar(255) DEFAULT NULL COMMENT 'Value', `row_id` int(10) unsigned NOT NULL COMMENT 'Version Id', PRIMARY KEY (`value_id`), UNIQUE KEY `CATALOG_PRODUCT_ENTITY_VARCHAR_ROW_ID_ATTRIBUTE_ID_STORE_ID` (`row_id`,`attribute_id`,`store_id`), KEY `CATALOG_PRODUCT_ENTITY_VARCHAR_ATTRIBUTE_ID` (`attribute_id`), KEY `CATALOG_PRODUCT_ENTITY_VARCHAR_STORE_ID` (`store_id`), CONSTRAINT `CATALOG_PRODUCT_ENTITY_VARCHAR_STORE_ID_STORE_STORE_ID` FOREIGN KEY (`store_id`) REFERENCES `store` (`store_id`) ON DELETE CASCADE, CONSTRAINT `CAT_PRD_ENTT_VCHR_ATTR_ID_EAV_ATTR_ATTR_ID` FOREIGN KEY (`attribute_id`) REFERENCES `eav_attribute` (`attribute_id`) ON DELETE CASCADE, CONSTRAINT `CAT_PRD_ENTT_VCHR_ROW_ID_CAT_PRD_ENTT_ROW_ID` FOREIGN KEY (`row_id`) REFERENCES `catalog_product_entity` (`row_id`) ON DELETE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=567985353 DEFAULT CHARSET=utf8 COMMENT='Catalog Product Varchar Attribute Backend Table'
表行数:2,813,210行。
多次执行EXPLAIN EXTENDED结果不同的核心原因如下:
统计信息波动或不准确
MySQL优化器依赖表的统计信息(如索引选择性、数据分布)生成执行计划。即使无主动数据变更,Aurora或MySQL后台可能异步刷新统计信息(比如自动执行ANALYZE TABLE的后台任务),或统计信息采样结果存在细微差异,导致优化器每次评估的候选索引集合不同,直接反映在possible_keys字段上。ON DUPLICATE KEY语句的特殊性
该语句本质是先尝试INSERT,再检查重复键以执行UPDATE。重复键检查阶段,所有包含唯一键/主键的索引、以及能快速定位重复行的普通索引,都可能被纳入候选范围。优化器会根据实时成本估算动态选择候选索引,导致possible_keys内容变化。优化器成本估算的细微波动
即使统计信息整体稳定,MySQL优化器的成本计算模型会参考系统运行时状态(如缓存命中率、当前内存可用量),这些细微变化可能导致优化器对不同索引的成本评估出现偏差,进而改变possible_keys的输出。MySQL 5.7版本的优化器特性
你使用的MySQL 5.7.12版本,其优化器处理UPSERT语句时,索引候选选择逻辑本身存在一定灵活性,不像高版本MySQL那样稳定。加上Aurora 2.11.1基于该版本的定制化实现,进一步放大了执行计划的波动。
内容的提问来源于stack exchange,提问作者Ian

