为何InnoDB执行count(customer_id)时选用idx_fk_store_id而非主键索引?
InnoDB执行
count(customer_id)时选择二级索引而非主键索引的原因 环境信息
- 操作系统:CentOS 7
- MySQL版本:5.7.39
- 数据库:sakila
- 目标表:customer
问题详情
执行语句select count(customer_id) from customer时,通过explain查看执行计划,发现InnoDB选择了二级索引idx_fk_store_id而非主键索引:
| type | key | Extra |
|---|---|---|
| index | idx_fk_store_id | Using index |
表的建表语句如下:
CREATE TABLE `customer`( `customer_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, `store_id` tinyint(3) unsigned NOT NULL, PRIMARY KEY(`customer_id`), KEY `idx_fk_store_id`(`store_id`) ) ENGINE=InnoDB AUTO_INCREMENT=600 DEFAULT CHARSET=utf8mb4
核心原因
这是MySQL优化器基于索引空间占用做出的最优选择,本质是为了减少IO开销:
- 索引结构差异
InnoDB的主键索引是聚簇索引,每条记录包含整行数据+额外元数据(事务ID、回滚指针等,约13字节);而二级索引仅存储索引列值+主键值+少量元数据。
在这个表中:
- 聚簇索引单条记录大小:
customer_id(2字节) +store_id(1字节) + 元数据(约13字节) = 约16字节 - 二级索引
idx_fk_store_id单条记录大小:store_id(1字节) +customer_id(2字节) + 少量元数据 = 约6字节
- 优化器的选择逻辑
当执行count(customer_id)时,因为customer_id是主键(自带非空约束),统计逻辑等价于count(*)——只需遍历索引统计非空行数即可。此时优化器会优先选择体积最小的索引,因为更小的索引意味着需要读取的数据页更少,IO成本更低。这里二级索引idx_fk_store_id的整体空间远小于聚簇索引,所以被优化器选中。
InnoDB不像MyISAM会维护内置的行数统计,所有count操作都需要遍历索引完成,优化器的核心目标就是选择成本最低的执行路径。
内容的提问来源于stack exchange,提问作者Joseph Needham
相关产品推荐
相关产品推荐

