まず、下のOTN Forumを読んでください。
Tom KyteがここでORA-01555エラーを起こす確実なテストケースを提供しています。でも、この歴史的に証明された確実なテストケースさえも問題を再現することに失敗しています。
なぜこんな確実なテストケースさえ再現に失敗するのか簡単な例で説明してみます。
1. 私のオラクル環境です。
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production
2. ORA-01555エラーが発生しやすく小さなサイズのUndo Tablespaceを作ります。
alter system set undo_tablespace=UNDOTBS1;
drop table t purge;
create table t as select * from all_objects;
create index t_idx on t(object_id);
drop tablespace small_undo including contents and datafiles;
create undo tablespace small_undo datafile size 200k;
alter system set undo_tablespace=SMALL_UNDO;
alter system flush buffer_cache;
3. Tom Kyteの下のテストケースは必ずORA-01555エラーを起こすようになっています。
UKJA@ukja102> begin
2 for x in ( select * from t where object_id > 0 )
3 loop
4 update t set object_name = lower(object_name)
5 where object_id = x.object_id and rownum=1;
6 commit;
7 end loop;
8 end;
9 /
PL/SQL procedure successfully completed. -- Why no ORA-01555???
でもエラーが発生しません。なぜそうですか。
4. Tom Kyteがテストケースを作った以前のバージョンとは違いに最近のバージョンのオラクルでは該当SQL文章がIndex Range ScanではないTable Full Scanを使用します。
explain plan for select * from t where object_id > 0;
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50000 | 4541K| 186 (17)| 00:00:03 |
|* 1 | TABLE ACCESS FULL| T | 50000 | 4541K| 186 (17)| 00:00:03 |
--------------------------------------------------------------------------
Table Full Scanをする場合には一つのブロックの内に入っている行たちを一回に読み込むから一度読んだブロックをまた読む場合がほとんどありません。従ってUpdateによって変更されたブロックを再訪問することがないし、従ってUndoを読む過程でORA-01555エラーに会う確率もとても低くなります。
5. 今、次のようにINDEXヒントを与えてIndex Range Scanを使用するようにします。ただし、1000件の行だけ生成します。この場合にもいぜんとしてORA-01555エラーが発生していません。
UKJA@ukja102> create table t as select * from all_objects where rownum <= &1;
old 1: create table t as select * from all_objects where rownum <= &1
new 1: create table t as select * from all_objects where rownum <= 1000
Table created.
UKJA@ukja102> begin
2 for x in ( select /*+ index(t) */ * from t where object_id > 0 )
3 loop
4 update t set object_name = lower(object_name)
5 where object_id = x.object_id and rownum=1;
6 commit;
7 end loop;
8 end;
9 /
PL/SQL procedure successfully completed. -- Again no ORA-01555 error?
6. 1000件の行が少なすぎてUndoを十分になぞって書けないからです。次のように10,000件の行を読むようにすればとうとうORA-01555エラーが発生します。
UKJA@ukja102> create table t as select * from all_objects where rownum <= &1;
old 1: create table t as select * from all_objects where rownum <= &1
new 1: create table t as select * from all_objects where rownum <= 10000
Table created.
UKJA@ukja102> begin
2 for x in ( select /*+ index(t) */ * from t where object_id > 0 )
3 loop
4 update t set object_name = lower(object_name)
5 where object_id = x.object_id and rownum=1;
6 commit;
7 end loop;
8 end;
9 /
begin
*
ERROR at line 1:
ORA-01555: snapshot too old: rollback segment number with name "" too small
ORA-06512: at line 2
7. 上で言った例の外にもORA-01555エラーが発生しない多様な可能性があるでしょう。
毎度感じるものですが、いつどこでも再現可能なテストケースを作るのがどれほど難しいかわかりません。皆さんも誰かにテストケースをあげる事があったらもしや思えなかった制限や欠点を持っていないか悩まなければいけません。
No comments:
Post a Comment