查看表扫描次数,并对比索引对表查询的作用

本文通过实验对比了不同索引设置对表扫描的影响。在347,758行数据的表上,测试了全表扫描及特定条件查询下的逻辑读取次数。结果显示,针对查询条件建立索引能显著提高查询效率。

1、什么是表扫描

当执行SQL 语句时,可通过“”评估执行计划”,查看语句的执行计划。尤其是语句设计查询,会出现“表扫描”部分;

表扫描是严重影响查询时间的因素!

2、验证

(1)新建数据表BasicMsg20161204,主键为自增列,但是将聚集索引建立在(RecvTime,AA,MsgTypecode)上;

(2)新建数据表BasicMsg20161104,主键为自增列,且该列为聚集索引;在RecvTime上有非聚集索引;

(3)新建数据表BasicMsg20161004,主键为自增列,且该列为聚集索引;没有任何其他索引。

        表中共有347758行数据, 查看三表的IO读写情况:

--//----------------------------------------------

--1、查看该表的IO读写情况

DBCC DROPCLEANBUFFERS --清空所有的缓存区内容
SET STATISTICS IO ON 
SELECT * FROM BasicMsg20161204 --WHERE IDFlag=347758
SET STATISTICS IO OFF

--2、结果:

--BasicMsg20161204
--(347758 row(s) affected)
--Table 'BasicMsg20161204'. Scan count 1, logical reads 6594, physical reads 2, read-ahead reads 6596, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--扫描计数1次,逻辑读取6594次!物理读取2次,预读6594次,lob逻辑读取0次...
--
BasicMsg20161104
--(347758 row(s) affected)
--Table 'BasicMsg20161104'. Scan count 1, logical reads 6575, physical reads 3, read-ahead reads 6596, 
--扫描计数1次,逻辑读取6575次!物理读取3次,预读6575次,lob逻辑读取0次...

--3、分析

  发现,当未用WHERE 语句对索引列进行条件筛选时,前两表的逻辑读取都比较大,索引没有明显作用;未加任何索引的1004表的预读次数反而少;

(1)当在查询语句后加 WHERE RecvTime<=17999995限制条件,扫描次数一样

(2)当在查询语句后加 WHERE RecvTime=17999995限制条件,BasicMsg20161204表的结果为:Scan count 1, logical reads 3, physical reads 3;建立非聚集索引的BasicMsg20161104表为:Scan count 1, logical reads 3, physical reads 3;

  未在RecvTime上加索引,1004表的Table 'BasicMsg20161004'. Scan count 9, logical reads 7215, physical reads 2, read-ahead reads 4352,与其他2表的3次和6次,有天壤之别!!

这说明,索引建立在需要具体判定条件的列上才有效。

 --4、DBCC命令协助理解

dbcc ind 命令查看下数据表记录在哪个数据页中

DBCC IND(DF17DataPro,BasicMsg20161104 ,-1) -- (数据库名,表名,参数值)

然后导出该数据页

dbcc traceon(3604)
dbcc page(Ctrip,1,148,1)

转载于:https://www.cnblogs.com/Miss-Bueno/p/7400268.html

<think>好的,我现在需要解决用户关于Oracle执行计划中二次回扫描代价巨大的问题。首先,用户可能已经了解执行计划的基本概念,但可能对回扫描的具体原因和影响不太清楚。我需要先确定二次回是什么,为什么会发生,以及为什么它的代价高。 二次回通常发生在使用索引访问数据后,还需要根据索引中的rowid去中获取其他列的数据。这种情况下,如果查询需要返回的列不在索引中,数据库就必须回到查询,这会导致额外的I/O操作,尤其是当数据量很大时,这种操作会非常耗时。 接下来,我需要考虑用户可能的背景。用户可能是数据库开发人员或DBA,遇到了性能问题,执行计划显示有二次回,他们想了解原因和优化方法。用户可能已经尝试过一些索引优化,但效果不佳,或者对索引覆盖不够了解。 用户可能没有明确提到的深层需求是如何优化这种情况,而不仅仅是了解原因。所以,在解释原因后,我需要给出具体的优化策略,比如创建覆盖索引、使用索引组织等。 在组织回答时,需要分步骤解释原因,然后给出解决方案。要确保语言通俗易懂,避免过于专业的术语,但又要准确。例如,解释回是什么,为什么会产生大量I/O,以及如何通过覆盖索引减少回次数。 还要考虑可能的误区,比如用户可能认为只要建索引就能解决问题,但实际上需要覆盖所有查询所需的列。此外,分区或调整块大小也可能有帮助,但需要具体情况具体分析。 最后,总结常见原因和优化方法,帮助用户系统地理解和解决问题。需要检查是否有遗漏的关键点,比如函数索引或统计信息的影响,确保回答全面。</think>在Oracle数据库中,**二次回扫描(TABLE ACCESS BY INDEX ROWID)** 是执行计划中常见的性能瓶颈,其代价巨大的原因及优化方案如下: --- ### **一、什么是二次回扫描?** 当使用索引定位到数据行的`ROWID`后,仍需通过`ROWID`回(访问原始数据块)获取其他未包含在索引中的列值。例如: ```sql -- 假设索引仅包含 `id` 和 `name` SELECT id, name, age FROM users WHERE name = 'Alice'; ``` 若`age`列不在索引中,即使通过索引快速定位到`name='Alice'`的行,仍需根据`ROWID`回获取`age`的值。这一过程称为**二次回**。 --- ### **二、为什么二次回代价巨大?** #### **1. I/O开销倍增** - **随机I/O**:索引中记录的`ROWID`对应的数据块通常是分散的(非连续物理存储),回时需多次随机读取磁盘。 - **数据块访问频率**:若目标行分布在1000个不同数据块中,需执行1000次物理I/O(假设数据未缓存)。 #### **2. CPU资源消耗** - 解析`ROWID`定位数据块需要CPU参与,大量回操作会导致CPU负载升高。 #### **3. 缓存失效风险** - 若数据未缓存在Buffer Cache中,回时需从磁盘读取,加剧I/O压力。 #### **4. 放大效应** - **回比例高**:若通过索引筛选出的行数占总行数的比例较大(如超过5%~10%),全扫描可能比索引更高效。 --- ### **三、如何判断二次回代价?** 通过执行计划观察关键指标: 1. **`TABLE ACCESS BY INDEX ROWID`**:明确存在回操作。 2. **`Cost`值**:执行计划中的成本估算。 3. **`A-Rows`和`Buffers`**:实际返回行数和逻辑读次数(可通过`/*+ GATHER_PLAN_STATISTICS */`获取)。 示例执行计划: ``` ------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 100 | 2300 | 102 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 100 | 2300 | 102 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_DEPT | 100 | | 2 (0)| 00:00:01 | ------------------------------------------------------------------------------------------ ``` --- ### **四、优化二次回的方法** #### **1. 创建覆盖索引(最有效)** 将查询中所有需要的列包含在索引中,避免回。 ```sql -- 原索引:CREATE INDEX idx_name ON users(name); -- 优化后:将age加入索引 CREATE INDEX idx_name_age ON users(name, age); ``` **适用场景**:查询频率高且返回列固定。 #### **2. 使用索引组织(IOT)** 将数据直接存储在索引结构中,消除回。 ```sql CREATE TABLE users_iot ( id NUMBER PRIMARY KEY, name VARCHAR2(50), age NUMBER ) ORGANIZATION INDEX; ``` **适用场景**:以主键为条件频繁查询整行数据。 #### **3. 减少返回列** 仅查询必要的列,避免访问非索引列。 ```sql -- 优化前:SELECT * FROM users WHERE name = 'Alice'; -- 优化后:SELECT id, name FROM users WHERE name = 'Alice'; ``` #### **4. 调整数据块大小** 增大数据块(如从8KB调整为32KB),提升单次IOP能缓存的数据量,减少物理I/O次数。 #### **5. 分区优化** 对按范围或哈希分区,结合本地分区索引,减少回范围。 ```sql CREATE TABLE sales ( sale_id NUMBER, sale_date DATE ) PARTITION BY RANGE (sale_date) ( PARTITION p2023 VALUES LESS THAN (TO_DATE('2024-01-01', 'YYYY-MM-DD')) ); CREATE INDEX idx_sales_date ON sales(sale_date) LOCAL; ``` #### **6. 使用内存缓存** - 将频繁访问的或分区缓存在KEEP池中: ```sql ALTER TABLE users STORAGE (BUFFER_POOL KEEP); ``` - 增加Buffer Cache大小: ```sql ALTER SYSTEM SET DB_CACHE_SIZE = 8G; ``` --- ### **五、对比优化效果** | **场景** | **逻辑读(Buffers)** | **物理读(Disk Reads)** | **响应时间** | |------------------------|-----------------------|--------------------------|--------------| | 未优化(回) | 10,000 | 500 | 2.5秒 | | 覆盖索引 | 200 | 0 | 0.02秒 | | 索引组织(IOT) | 150 | 0 | 0.01秒 | --- ### **六、总结** $$ \text{二次回代价高} \propto \text{回行数} \times \text{单行I/O成本} $$ **核心优化逻辑**: 1. **减少回次数**:通过覆盖索引或IOT直接返回数据。 2. **降低单次I/O成本**:提升缓存命中率、优化数据存储结构。 3. **权衡索引维护代价**:覆盖索引可能增加写操作开销,需根据业务读写比例权衡。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值