Oracle开发者性能课第6课(如何创建物化视图)实验
Posted dingdingfish
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle开发者性能课第6课(如何创建物化视图)实验相关的知识,希望对你有一定的参考价值。
概述
本实验参考DevGym中的实验指南。
创建环境
创建表bricks和索引:
exec dbms_random.seed ( 0 );
create table bricks (
brick_id not null constraint bricks_pk primary key,
colour not null,
shape not null,
weight not null,
insert_date not null,
junk default lpad ( 'x', 50, 'x' ) not null
) as
with rws as (
select level x from dual
connect by level <= 10000
)
select rownum brick_id,
case ceil ( rownum / 2500 )
when 4 then 'red'
when 1 then 'blue'
when 2 then 'green'
when 3 then 'yellow'
end colour,
case mod ( rownum, 4 )
when 0 then 'cube'
when 1 then 'cylinder'
when 2 then 'pyramid'
when 3 then 'prism'
end shape,
round ( dbms_random.value ( 1, 10 ) ),
date'2020-01-01' + ( rownum/24 ) + ( mod ( rownum, 24 ) / 36 ) insert_date,
lpad ( 'x', 50, 'x' )
from rws;
查看数据,bricks表有10000行:
SQL> select count(*) from bricks;
COUNT(*)
___________
10000
SQL>
SELECT
brick_id,
colour,
shape,
weight,
insert_date
FROM
bricks
WHERE
ROWNUM <= 9;
BRICK_ID COLOUR SHAPE WEIGHT INSERT_DATE
___________ _________ ___________ _________ ______________
1 blue cylinder 2 01-JAN-20
2 blue pyramid 8 01-JAN-20
3 blue prism 3 01-JAN-20
4 blue cube 3 01-JAN-20
5 blue cylinder 4 01-JAN-20
6 blue pyramid 2 01-JAN-20
7 blue prism 5 01-JAN-20
8 blue cube 10 01-JAN-20
9 blue cylinder 9 01-JAN-20
9 rows selected.
数百万行的聚合
报表查询通常将许多行聚合到少数行。例如:
select /*+ gather_plan_statistics */colour, count(*)
from bricks
group by colour;
COLOUR COUNT(*)
_________ ___________
green 2500
red 2500
yellow 2500
blue 2500
select * from table(dbms_xplan.display_cursor( format => 'iosTATS LAST'));
PLAN_TABLE_OUTPUT
__________________________________________________________________________________________
SQL_ID 8qjfk54pd67ya, child number 0
-------------------------------------
select /*+ gather_plan_statistics */colour, count(*) from bricks
group by colour
Plan hash value: 1511612015
---------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 4 |00:00:00.01 | 119 |
| 1 | HASH GROUP BY | | 1 | 4 | 4 |00:00:00.01 | 119 |
| 2 | TABLE ACCESS FULL| BRICKS | 1 | 10000 | 10000 |00:00:00.01 | 119 |
---------------------------------------------------------------------------------------
15 rows selected.
此时可以创建摘要表,以提高查询效率:
create table colour_summary as
select colour, count(*) c
from bricks
group by colour;
select /*+ gather_plan_statistics */*
from colour_summary ;
select * from table(dbms_xplan.display_cursor( format => 'IOSTATS LAST'));
PLAN_TABLE_OUTPUT
__________________________________________________________________________________________________________
SQL_ID 2j0426s6cky3v, child number 0
-------------------------------------
select /*+ gather_plan_statistics */* from colour_summary
Plan hash value: 241215016
-------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
-------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 4 |00:00:00.01 | 3 | 5 |
| 1 | TABLE ACCESS FULL| COLOUR_SUMMARY | 1 | 4 | 4 |00:00:00.01 | 3 | 5 |
-------------------------------------------------------------------------------------------------------
13 rows selected.
但问题是,基表改变时,摘要表也需要相应改变。
使用物化视图创建摘要
注意执行计划中的MAT_VIEW ACCESS FULL:
create materialized view brick_colours_mv
as
select colour, count(*)
from bricks
group by colour;
select /*+ gather_plan_statistics */* from brick_colours_mv;
SQL> select * from table(dbms_xplan.display_cursor( format => 'IOSTATS LAST'));
PLAN_TABLE_OUTPUT
______________________________________________________________________________________________________
SQL_ID 2v361dp2652xg, child number 0
-------------------------------------
select /*+ gather_plan_statistics */* from brick_colours_mv
Plan hash value: 3196177224
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 4 |00:00:00.01 | 3 |
| 1 | MAT_VIEW ACCESS FULL| BRICK_COLOURS_MV | 1 | 4 | 4 |00:00:00.01 | 3 |
---------------------------------------------------------------------------------------------------
13 rows selected.
和逻辑视图不同,物化视图就是物理表:
SQL> select segment_type from user_segments where segment_name = 'BRICK_COLOURS_MV';
SEGMENT_TYPE
_______________
TABLE
可是目前为止,我们创建的物化视图还不能与基表同步。例如我们可以把表删除:
SQL> drop table bricks;
Table BRICKS dropped.
SQL> select /*+ gather_plan_statistics */* from brick_colours_mv;
COLOUR COUNT(*)
_________ ___________
green 2500
red 2500
yellow 2500
blue 2500
好吧,我们利用最初的语句恢复bricks表,并重建物化视图。
自动使用物化视图:查询重写(Query Rewrite)
Query Rewrite是优化器的一项功能,目的是为了不修改代码。如果运行了物化视图中使用的查询,优化器可以检测到这一点。并将查询更改为使用MV而不是基表!
需要启用Query Rewrite:
SQL> alter materialized view brick_colours_mv enable query rewrite;
Materialized view BRICK_COLOURS_MV altered.
然后就生效了,注意执行计划中的MAT_VIEW REWRITE:
select /*+ gather_plan_statistics */colour, count(*) c
from bricks
group by colour;
select *
from table(dbms_xplan.display_cursor( format => 'IOSTATS LAST'));
PLAN_TABLE_OUTPUT
______________________________________________________________________________________________________________
SQL_ID 76wkhk51yu99d, child number 0
-------------------------------------
select /*+ gather_plan_statistics */colour, count(*) c from bricks
group by colour
Plan hash value: 1470497824
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 4 |00:00:00.01 | 3 |
| 1 | MAT_VIEW REWRITE ACCESS FULL| BRICK_COLOURS_MV | 1 | 4 | 4 |00:00:00.01 | 3 |
-----------------------------------------------------------------------------------------------------------
14 rows selected.
对于可以由物化视图的结果派生出的其它查询,优化器也可以使用查询重写。例如:
select /*+ gather_plan_statistics */count(*) row#
from bricks
where colour = 'red'
group by colour;
select * from table(dbms_xplan.display_cursor( format => 'IOSTATS LAST'));
数据更改的影响
默认情况下,只有物化视图和基表的数据一致时,优化器才会考虑使用查询重写。
例如,对基表新增1行,优化器不再使用查询重写。
insert into bricks values ( 0, 'red', 'cube', 100, sysdate, default );
commit;
SQL>
select /*+ gather_plan_statistics */colour, count(*) num_rows
from bricks
group by colour;
COLOUR NUM_ROWS
_________ ___________
green 2500
red 2501
yellow 2500
blue 2500
SQL> select * from table(dbms_xplan.display_cursor( format => 'IOSTATS LAST'));
PLAN_TABLE_OUTPUT
__________________________________________________________________________________________
SQL_ID dp1b2x0mw27tv, child number 1
-------------------------------------
select /*+ gather_plan_statistics */colour, count(*) num_rows from
bricks group by colour
Plan hash value: 1511612015
---------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 4 |00:00:00.01 | 121 |
| 1 | HASH GROUP BY | | 1 | 4 | 4 |00:00:00.01 | 121 |
| 2 | TABLE ACCESS FULL| BRICKS | 1 | 10000 | 10001 |00:00:00.01 | 121 |
---------------------------------------------------------------------------------------
15 rows selected.
使用陈旧数据
在某些情况下,您可能仍然希望使用过时的物化视图。例如,当查询旧数据时,您知道物化视图中的数据是正确的。或者,与其等很长时间等待结果,不如尽快得到过时的答案。
通过调整会话设置query_rewrite_integrity,您可以允许优化器在这些情况下使用过时的物化视图。
以下为示例:
SQL> show parameter query_rewrite_integrity
NAME TYPE VALUE
----------------------- ------ --------
query_rewrite_integrity string enforced
SQL> alter session set query_rewrite_integrity = stale_tolerated;
Session altered.
SQL> select /* stale */count(*) c
2 from bricks;
C
________
10000
SQL> select * from table(dbms_xplan.display_cursor( format => 'IOSTATS LAST'));
PLAN_TABLE_OUTPUT
____________________________________________________________________________________
SQL_ID guxqvbznnfh9f, child number 0
-------------------------------------
select /* stale */count(*) c from bricks
Plan hash value: 2494147771
-------------------------------------------------------------------
| Id | Operation | Name | E-Rows |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | |
| 1 | SORT AGGREGATE | | 1 |
| 2 | MAT_VIEW REWRITE ACCESS FULL| BRICK_COLOURS_MV | 4 |
-------------------------------------------------------------------
Note
-----
- Warning: basic plan statistics not available. These are only collected when:
* hint 'gather_plan_statistics' is used for the statement or
* parameter 'statistics_level' is set to 'ALL', at session or system level
20 rows selected.
将query_rewrite_integrity修改为默认,此时的查询结果就是最新的了。
SQL> alter session set query_rewrite_integrity = enforced;
Session altered.
SQL> select /* enforced */count(*)
2 from bricks;
COUNT(*)
___________
10001
使物化视图与基表保持一致
可以手动刷新,C表示Complete,也就是全量刷新。等于重新运行物化视图中的查询:
SQL> exec dbms_mview.refresh ( 'brick_colours_mv', 'C' );
PL/SQL procedure successfully completed.
物化视图快速刷新
也称为增量刷新。需要建立物化视图日志。其默认表名为MLOG$_<tablename>
:
create materialized view log
on bricks
with primary key, rowid, sequence, commit scn (
colour, shape, weight, insert_date
)
including new values;
Materialized view log BRICKS created.
修改基础表,然后可以看到日志中的修改记录:
update bricks
set colour = 'green'
where brick_id = 1;
delete bricks
where brick_id = 2;
insert into bricks values ( -2, 'red', 'cube', 100, sysdate, default );
commit;
select * from mlog$_bricks;
BRICK_ID COLOUR SHAPE WEIGHT INSERT_DATE M_ROW$$ SEQUENCE$$ DMLTYPE$$ OLD_NEW$$ CHANGE_VECTOR$$ XID$$
___________ _________ ___________ _________ ______________ _____________________ _____________ ____________ ____________ __________________ ___________________
1 blue cylinder 2 01-JAN-20 AAASeTAAMAAA6AjAAA 1 U O 04 1125964331378063
1 green cylinder 2 01-JAN-20 AAASeTAAMAAA6AjAAA 2 U N 04 1125964331378063
2 blue pyramid 8 01-JAN-20 AAASeTAAMAAA6AjAAB 3 D O 00 1125964331378063
-2 red cube 100 30-OCT-21 AAASeTAAMAAA6EmAAB 4 I N FE 1125964331378063
有了物化视图日志后,就有可能使用增量刷新了。为什么是有可能?因为这个日志并没有记录所有的变化,例如junk列。因此有时还需要使用全量刷新。
但目前的刷新仍需要手动来做。因为默认的刷新子句为REFRESH FORCE ON DEMAND
。
可以设置在每次事务提交时做自动刷新,但首先必须做一次全量刷新:
exec dbms_mview.refresh ( 'brick_colours_mv', 'C' );
alter materialized view brick_colours_mv
refresh fast on commit;
现在,物化视图和基表就可以保持完全一致了:
select *
from brick_colours_mv;
COLOUR COUNT(*)
------ ----------
green 2501
red 2502
yellow 2500
blue 2498
insert into bricks values ( -1, 'red', 'cube', 100, sysdate, default );
commit;
select *
from brick_colours_mv;
COLOUR COUNT(*)
------ ----------
green 2501
red 2503
yellow 2500
blue 2498
快速刷新的限制
详细的限制见这里,例如不能使用COUNT ( DISTINCT ):
create materialized view bricks_not_fast_mv
as
select colour, count(*), count ( distinct shape )
from bricks
group by colour;
alter materialized view bricks_not_fast_mv
refresh fast on commit;
Error starting at line : 1 in command -
alter materialized view bricks_not_fast_mv
refresh fast on commit
Error report -
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view
12054. 00000 - "cannot set the ON COMMIT refresh attribute for the materialized view"
*Cause: The materialized view did not satisfy conditions for refresh at
commit time.
*Action: Specify only valid options.
使用dbms_mview.explain_mview可以查看允许的刷新方式。
而且对于重度交易系统,快速刷新还会增加事务提交时的开销,刷新会太频繁,需要慎重考虑。
最好是通过查询实现刷新,Oracle Database 12.2通过实时物化视图可以做到。
实时物化视图
本质是在查询时刷新,而非在事务提交时。
实时物化视图允许查询使用过时的数据。在查询运行时,数据库应用物化视图日志中的更改以保证物化视图数据的实时性。此功能要求物化视图:
- 属性为FAST REFRESH ON DEMAND
- 使用ENABLE ON QUERY COMPUTATION 子句
可以通过以下SQL设置:
alter materialized view brick_colours_mv
refresh fast on demand;
alter materialized view brick_colours_mv
enable on query computation;
然后我们修改一些数据,此时物化视图的数据过时。
insert into bricks values ( -3, 'red', 'cube', 100, sysdate, default );
commit;
select * from mlog$_bricks;
BRICK_ID COLOUR SHAPE WEIGHT INSERT_DATE M_ROW$$ SEQUENCE$$ DMLTYPE$$ OLD_NEW$$ CHANGE_VECTOR$$ XID$$
___________ _________ ________ _________ ______________ _____________________ _____________ ____________ ____________ __________________ ___________________
-3 red cube 100 30-OCT-21 AAASeqAAMAABD42AAD 13 I N FE Oracle开发者性能课第4课(如何创建索引)实验