sql – 使用Oracle的大量预计I / O,即使仅获取单个记录
我经常在我的Oracle执行计划中遇到以下情况:
Operation | Object | Order | Rows | Bytes | Projection ----------------------------+---------+-------+------+-------+------------- TABLE ACCESS BY INDEX ROWID | PROD | 7 | 2M | 28M | PROD.VALUE INDEX UNIQUE SCAN | PROD_PK | 6 | 1 | | PROD.ROWID 这是一个更大的执行计划的摘录.本质上,我正在使用表的主键访问(加入)一个表.通常,还有另一个表ACCO与ACCO.PROD_ID = PROD.ID,其中PROD_PK是PROD.ID的主键.显然,可以使用独特的扫描程序来访问表格,但一旦我在表上有一些愚蠢的投影,就好像整个表格(大约200万行)都被计划在内存中读取.我得到了很多I / O和缓冲区.当我从更大的查询中删除投影时,问题消失了: Operation | Object | Order | Rows | Bytes | Projection ----------------------------+---------+-------+------+-------+------------- TABLE ACCESS BY INDEX ROWID | PROD | 7 | 1 | 8 | PROD.ID INDEX UNIQUE SCAN | PROD_PK | 6 | 1 | | PROD.ROWID 我不明白这个行为.这可能是什么原因?注意,我无法发布完整的查询.这是相当复杂的,涉及很多的计算.然而,这种模式往往是一样的. 更新:我马上将我相当复杂的设置放在一个简单的模拟中,在两种情况下(当投影PROD.VALUE或离开它时)都会产生类似的执行计划: 创建以下数据库: -- products have a value create table prod as select level as id,10 as value from dual connect by level < 100000; alter table prod add constraint prod_pk primary key (id); -- some products are accounts create table acco as select level as id,level as prod_id from dual connect by level < 50000; alter table acco add constraint acco_pk primary key (id); alter table acco add constraint acco_prod_fk foreign key (prod_id) references prod (id); -- accounts have transactions with values create table trxs as select level as id,mod(level,10) + 1 as acco_id,17) + 1 as value from dual connect by level < 100000; alter table trxs add constraint trxs_pk primary key (id); alter table trxs add constraint trxs_acco_fk foreign key (acco_id) references acco (id); create index acco_i on acco(prod_id); create index trxs_i on trxs(acco_id); alter table acco modify prod_id not null; alter table trxs modify acco_id not null; 运行以下查询 select v2.*
from (
select
-- This calculates the balance for every transaction as a
-- running total,subtracting trxs.value from the product's value
--
-- This is the "projection" I mentioned that causes I/O. Leaving it
-- away (setting it to 0),would improve the execution plan
prod.value - v1.total balance,acco.id acco_id
from (
select
acco_id,sum(value) over (partition by acco_id
order by id
rows between unbounded preceding
and current row) total
from trxs
) v1
join acco on v1.acco_id = acco.id
join prod on acco.prod_id = prod.id
) v2
-- This is the single-row access predicate. From here,it is
-- clear that there can only be 1 acco and 1 prod
where v2.acco_id = 1;
分析 当分析上述查询的执行计划(有或没有任何prod.value投影)时,在访问prod表时,我可以在计划中重现过多的行/字节. 我找到了一个workaround for this issue.但是我真的很感兴趣的是解释出了什么问题,以及如何可以纠正这个问题,而不用改变查询太多 更新 好的,经过更多的分析,我不得不说,实际有问题的I / O是由于错误的索引被用于其他地方.不幸的是,在总体统计(或执行计划)中预测的这个数字还不够好. 就这个问题而言,我仍然对执行计划中的预计I / O感到好奇,因为这似乎混淆了我们的DBA(和我)一次又一次.有时,它真的是I / O问题的根源… 解决方法可能有趣的是,我已经检查了各种场景,包括具体的具体方案.在这种情况下,将样例查询重新排序为这样可以解决问题:select
-- Explicitly project value in a nested loop. This seems to be much cheaper
-- in this specific case
(select value from prod where id = v2.prod_id) - v2.balance,v2.acco_id
from (
select
-- Now,balance is only a running total,not the running total
-- added to PROD.VALUE
v1.total balance,acco.id acco_id,acco.prod_id prod_id
from (
select
acco_id,sum(value) over (partition by acco_id
order by id
rows between unbounded preceding
and current row) total
from trxs
) v1
-- The JOIN of PROD is no longer needed
join acco on v1.acco_id = acco.id
) v2
where v2.acco_id = 1;
但是我仍然不明白为什么Oracle会在其执行计划中投入太多的I / O,如果我在此查询中加入了prod … (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
