什么时候全表扫描比使用索引扫描能更快?

本文通过具体场景解析Oracle SQL优化器如何决定采用全表扫描而非索引扫描的方式获取数据,揭示了背后的技术逻辑。
伟大的Oracle SQL优化器可以判断出在某些情况下,使用全表扫描比使用索引扫描能更快的得到数据结果。
有没有想过,她是怎么做到的呢?
背后的原理是什么呢?

举一个非常好理解的场景(scenario:通过索引读取表中20%的数据)解释一下这个有趣的概念:

假设一张表含有10万行数据--------100000行
我们要读取其中20%(2万)行数据----20000行
表中每行数据大小80字节----------80bytes
数据库中的数据块大小8K----------8000bytes
所以有以下结果:
每个数据块包含100行数据---------100行
这张表一共有1000个数据块--------1000块

上面列出了一系列浅显易懂的数据,我们挖掘一下这些数据后面的故事:

通过索引读取20000行数据 = 约20000个table access by rowid = 需要处理20000个块来执行这个查询
但是,请大家注意:整个表只有1000个块!
所以:如果按照索引读取全部的数据的20%相当于将整张表平均读取了20次!!So,这种情况下直接读取整张表的效率会更高。很幸运,Oracle也是这么想的:)

-- The End --
# 执行计划选择全表扫描而非索引扫描的原因分析 ## 1. 数据库优化器的成本模型 数据库优化器通过**成本计算**决定使用全表扫描还是索引扫描,主要考虑以下因素: ### I/O成本 - 全表扫描:顺序读取数据页 - 索引扫描:随机读取索引页+数据页 ### CPU成本 - 全表扫描:简单但数据量大 - 索引扫描索引处理+行定位 ## 2. 全表扫描更优的典型场景 ### 高选择率查询 ```sql -- 当查询条件匹配表中大部分数据时(如>30%) SELECT * FROM employees WHERE salary < 10000; ``` ### 小表查询 ```sql -- 小表(如<10页)的全表扫描成本很低 SELECT * FROM config_settings WHERE parameter_name = 'timeout'; ``` ### 无有效索引可用 ```sql -- 查询条件涉及未索引列 SELECT * FROM orders WHERE customer_notes LIKE '%urgent%'; ``` ## 3. 执行计划中的判断依据 ### 观察EXPLAIN输出 ```sql EXPLAIN SELECT * FROM products WHERE category = 'Electronics'; -- 全表扫描通常会显示: -- type: ALL -- key: NULL -- rows: 表总行数 ``` ### 关键指标对比 | 指标 | 全表扫描 | 索引扫描 | |------|----------|----------| | 访问类型 | ALL | range/ref/index | | 预估行数 | 接近表总量 | 选择性的行数 | | Extra列 | 通常为空 | Using index | ## 4. 优化器的内部决策因素 ### 统计信息的影响 - 表的总行数 - 数据分布直方图 - 索引的选择性(不同值的比例) ### 系统配置参数 - `random_page_cost`(随机I/O成本因子) - `seq_page_cost`(顺序I/O成本因子) - `effective_cache_size`(缓存大小假设) ## 5. 强制使用索引的方法(需谨慎) ```sql -- MySQL语法 SELECT * FROM products FORCE INDEX(category_idx) WHERE category = 'Electronics'; -- PostgreSQL语法 SELECT * FROM products WITH (INDEX(category_idx)) WHERE category = 'Electronics'; -- SQL Server语法 SELECT * FROM products WITH (INDEX(category_idx)) WHERE category = 'Electronics'; ``` ## 6. 性能对比测试方法 ### 实际执行对比 ```sql -- 记录执行时间 SET STATISTICS TIME ON; SELECT * FROM products WHERE category = 'Electronics'; -- 全表扫描 SELECT * FROM products WITH (INDEX(category_idx)) WHERE category = 'Electronics'; -- 强制索引 ``` ### 分析执行计划差异 ```sql EXPLAIN ANALYZE SELECT * FROM products WHERE category = 'Electronics'; ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值