mysql扫描方法的基本类型
mysql执行查询时常见的扫描方法主要分为全表扫描和索引扫描两类。全表扫描会逐行读取表中所有记录,判断是否符合查询条件;索引扫描则是通过已有的索引结构快速定位符合条件的记录,避免扫描无关数据。

全表扫描的适用场景
全表扫描的执行逻辑是顺序读取表的所有数据页,对每一行数据进行条件匹配。当满足以下情况时,mysql通常会选择全表扫描:
- 查询没有可用的合适索引,比如查询条件中的字段没有建立任何索引
- 表的数据量非常小,全表扫描的成本低于索引扫描的IO成本
- 查询需要返回表中大部分数据,比如查询条件匹配了超过30%左右的表记录,此时索引扫描的回表成本会超过全表扫描
- 查询条件对索引字段使用了函数、运算或者类型转换,导致索引失效
索引扫描的适用场景
索引扫描会先通过索引树定位符合条件的索引项,再根据索引项中的主键ID回表获取完整记录(如果是覆盖索引则不需要回表)。当满足以下情况时,mysql更倾向于选择索引扫描:
- 查询条件命中了合适的索引,且条件匹配的记录占全表比例较低
- 查询只需要返回索引中包含的字段,也就是覆盖索引场景,不需要额外回表
- 查询涉及排序、分组操作,且排序分组的字段是索引的有序字段,可以利用索引的有序性避免额外排序
查询优化器的选择逻辑
mysql的查询优化器会基于统计信息和成本模型来评估不同扫描方法的代价,核心逻辑是选择预估执行成本最低的方案。成本主要包含IO成本和CPU成本两部分:
- IO成本:读取数据页、索引页产生的磁盘IO开销,随机IO的成本高于顺序IO
- CPU成本:对记录进行条件判断、排序等操作产生的CPU开销
统计信息的影响
优化器依赖表的统计信息来估算不同扫描方法的成本,统计信息包括表的行数、索引的基数(不同值的数量)、数据分布等。可以通过以下语句查看表的统计信息:
-- 查看表的统计信息 SHOW TABLE STATUS LIKE 'user_table'; -- 查看索引的统计信息 SHOW INDEX FROM user_table;
如果统计信息不准确,可能会导致优化器做出错误的扫描方法选择,此时可以通过ANALYZE TABLE语句更新统计信息:
-- 更新user_table的统计信息 ANALYZE TABLE user_table;
强制指定扫描方法的方式
如果优化器选择的扫描方法不符合预期,开发者可以通过提示语句强制指定扫描方式:
-- 强制全表扫描 SELECT /*+ FULL(user_table) */ * FROM user_table WHERE age > 20; -- 强制使用指定索引扫描 SELECT /*+ INDEX(user_table idx_age) */ * FROM user_table WHERE age > 20;
常见扫描方法选择案例分析
下面通过一个实际案例来展示不同查询条件下mysql的扫描方法选择:
-- 创建测试表
CREATE TABLE user_table (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
age INT,
create_time DATETIME,
INDEX idx_age (age),
INDEX idx_create_time (create_time)
);
-- 插入10万条测试数据
DELIMITER //
CREATE PROCEDURE insert_test_data()
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i < 100000 DO
INSERT INTO user_table (name, age, create_time)
VALUES (CONCAT('user_', i), FLOOR(RAND() * 100), DATE_ADD(NOW(), INTERVAL FLOOR(RAND() * 365) DAY));
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
CALL insert_test_data();
案例1:小范围查询命中索引
查询年龄小于25的用户,此时匹配的记录占比低,优化器会选择索引扫描:
-- 查看执行计划 EXPLAIN SELECT * FROM user_table WHERE age < 25;
执行计划的type字段会显示range,说明使用了索引范围扫描。
案例2:大范围查询触发全表扫描
查询年龄小于80的用户,此时匹配的记录占全表大部分,优化器会选择全表扫描:
-- 查看执行计划 EXPLAIN SELECT * FROM user_table WHERE age < 80;
执行计划的type字段会显示ALL,说明使用了全表扫描。
案例3:索引失效导致全表扫描
对索引字段使用函数,导致索引无法使用,触发全表扫描:
-- 查看执行计划,对age做运算导致索引失效 EXPLAIN SELECT * FROM user_table WHERE age + 1 < 25;
执行计划的type字段会显示ALL,说明使用了全表扫描。
优化扫描方法选择的建议
为了让mysql选择更合理的扫描方法,提升查询性能,可以参考以下建议:
- 为高频查询的字段建立合适的索引,避免索引缺失导致全表扫描
- 避免在索引字段上使用函数、运算或者隐式类型转换,防止索引失效
- 尽量使用覆盖索引,减少回表带来的额外IO成本
- 定期更新表的统计信息,保证优化器的成本评估准确
- 对于返回大量数据的查询,评估是否真的需要返回全部数据,尽量增加更精确的查询条件