1、解释热表上超出期望的I/O
在生产环境中在一个大负载条件下,一个查询使用的I/O比你在测试或开发系统时观察到的I/O要多得多,而你无法解释这一现象。
然后,你再在测试环境中恢复这个生产实例,却发现I/O又降下来了。先从一个非常小的表开始:
u1@ORCL> create table t ( x int );
表已创建。
u1@ORCL> insert into t values ( 1 );
已创建 1 行。
u1@ORCL> exec dbms_stats.gather_table_stats( user, 'T' );
PL/SQL 过程已成功完成。
u1@ORCL> select * from t;
X
----------
1
下面,将会话设置为使用serializable隔离级别,这样无论在会话中运行多少次查询,都将得到事务开始时刻的查询结果:
u1@ORCL> alter session set isolation_level=serializable;
会话已更改。
u1@ORCL> set autotrace on statistics
u1@ORCL> select * from t;
X
----------
1
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
420 bytes sent via SQL*Net to client
416 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
由此可见,完成这个查询使用了7个I/O(一致获取,consistent gets)。在另一个会话中,反复修改这个表:
u1@ORCL> begin
2 for i in 1 .. 10000
3 loop
4 update t set x = x+1;
5 commit;
6 end loop;
7 end;
8 /
PL/SQL 过程已成功完成。
再返回到前面的serializable会话,重新运行同样的查询:
u1@ORCL> select * from t;
X
----------
1
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
10012 consistent gets
0 physical reads
0 redo size
420 bytes sent via SQL*Net to client
416 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
这一次执行了10012次I/O,简直有天壤之别。如果你想再次运行select * from t;,可能会看到I/O再次下降到7;不再是10012。缓存区实际上可能在内存中包含同一个块的多个版本。
u1@ORCL> select file#,block#,count(*)
2 from v$bh
3 group by file#,block#
4 having count(*) > 3
5 order by 3;
FILE# BLOCK# COUNT(*)
---------- ---------- ----------
2 58106 4
1 77774 4
2 58108 4
1 77777 4
2 2807 5
4 8870 5
2 3267 5
2 60833 6
4 8886 6
已选择9行。
2、查看重启动
u1@ORCL> create table t ( x int, y int );
表已创建。
u1@ORCL> insert into t values ( 1, 1 );
已创建 1 行。
u1@ORCL> commit;
提交完成。
为了观察查看重启动,只需要一个触发器打印输出一些信息。使用一个before update on t for each触发器打印出行的前映象和作为更新结果的后映象:
u1@ORCL> create or replace trigger t_bufer
2 before update on t for each row
3 begin
4 dbms_output.put_line
5 ( 'old.x = ' || :old.x ||
6 ', old.y = ' || :old.y );
7 dbms_output.put_line
8 ( 'new.x = ' || :new.x ||
9 ', new.y = ' || :new.y );
10 end;
11 /
触发器已创建
u1@ORCL> update t set x = x+1;
old.x = 1, old.y = 1
new.x = 2, new.y = 1
已更新 1 行。
到此为止,一切都不出所料:触发器每触发一次,都可以看到旧值和新值。不过,需要注意,此时还没有提交,这一行任被锁定。在另一个会话中,执行一下更新:
u1@ORCL> update t set x = x+1 where x > 0;
当然,这回立即阻塞,因为第一次会话将这一行锁住了。如果现在回到第一个会话,并提交,会看到第二个会话中有以下输出:
u1@ORCL> update t set x = x+1 where x > 0;
old.x = 1, old.y = 1
new.x = 2, new.y = 1
old.x = 2, old.y = 1
new.x = 3, new.y = 1
已更新 1 行。
如果你又一个触发器会做一些非事务性的事情,这可能就是一个相当验证的问题。