下面的实验主要说明了:
(1)直接路径insert和间接路径insert的原理和区别
(2)直接加载与redo的关系
(3)直接加载与undo的关系
(4)直接加载与index的关系
直接路径insert方式:
insert /*+ append */ into t values ....
间接路径inset方式:
insert into t values ....
一 直接路径insert和间接路径insert的原理和区别
--创建测试表t
SQL> create table t (id int);
Table created.
SQL> insert into t values (1);
1 row created.
SQL> insert into t values (2);
1 row created.
SQL> insert into t values (3);
1 row created.
SQL> insert into t values (4);
1 row created.
SQL> insert into t values (5);
1 row created.
SQL> commit;
Commit complete.
--重启实例
SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup;
ORACLE instance started.
Total System Global Area 626327552 bytes
Fixed Size 2215944 bytes
Variable Size 197136376 bytes
Database Buffers 423624704 bytes
Redo Buffers 3350528 bytes
Database mounted.
Database opened.
--检查buffer_cache中是否有包含T表的块
SQL> select file#,block# from v$bh where objd=(select object_id from user_objects where object_name='T');
no rows selected
--通过间接路径插入
SQL> insert into t values (6);
1 row created.
SQL> insert into t values (7);
1 row created.
SQL> insert into t values (8);
1 row created.
SQL> commit;
Commit complete.
--提交后在检查buffer_cache
SQL> select file#,block# from v$bh where objd=(select object_id from user_objects where object_name='T');
FILE# BLOCK#
---------- ----------
1 105344
1 105344
1 105345
查询t表的行分布情况
SQL> select dbms_rowid.rowid_block_number(rowid) from t;
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------------
105345
105345
105345
105345
105345
105345
105345
105345
8 rows selected.
从上面结果可以看出数据块105345已经被读入到buffer_cache中,可以分析出明间接路径insert是先将数据块从磁盘读入buffer_cache,在buffer_cache中修改后在写回到磁盘
--查看t表的高水位点
SQL> select header_file,header_block from dba_segments where segment_name='T' and owner='SYS';
HEADER_FILE HEADER_BLOCK
----------- ------------
1 105344
SQL> select tracefile from v$process where addr=(select paddr from v$session where sid=(select sid from v$mystat where rownum=1));
TRACEFILE
--------------------------------------------------------------------------------
/u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_4931.trc
SQL> alter system dump datafile 1 block 105344;
System altered.
[oracle@localhost ~]$ cat /u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_4931.trc | grep Highwater:
Highwater:: 0x00419b82 ext#: 0 blk#: 1 ext size: 7
SQL> select dbms_utility.data_block_address_block(to_number('00419b82','xxxxxxxx')) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(TO_NUMBER('00419B82','XXXXXXXX'))
-----------------------------------------------------------------------
105346
从上面看出前面读入buffer_cache中的数据块105344是段头块,105345是数据块。这里105346是高水位点
-- 继续插入2行
SQL> insert into t values (9);
1 row created.
SQL> insert into t values (10);
1 row created.
SQL> commit;
Commit complete.
SQL> select file#,block# from v$bh where objd=(select object_id from user_objects where object_name='T');
FILE# BLOCK#
---------- ----------
1 105344
1 105344
1 105345
SQL> select dbms_rowid.rowid_block_number(rowid) from t;
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------------
105345
105345
105345
105345
105345
105345
105345
105345
105345
105345
10 rows selected.
这两行数据还是保存到105345数据块,可以通过前面dump的方式发现高水位点还是在105346上
在重启数据库使用直接路径insert
--清空表,我们知道delete高水位点不会下降
SQL> delete from t;
10 rows deleted.
SQL> commit;
Commit complete.
--再重启数据库使用直接路径插入
SQL> select file#,block# from v$bh where objd=(select object_id from dba_objects where object_name='T' and owner='SYS');
no rows selected --buffer_cache中没有t表的数据
--像t表插入数据(注意:由于现在的高水为在105346这个块上,如果我们插入数据量不够填满这个块的话通过查询的方式看不出什么效果,需要将这个块dump出来观察直接路径insert和间接路径insert的区别,这里我插入了多行数据)
insert /*+ append */ into t select rownum id from dba_objects where rownum<1000;
--查看buffer_cache,发现只有段头块
SQL> select file#,block# from v$bh where objd=(select object_id from user_objects where object_name='T');
FILE# BLOCK#
---------- ----------
1 105344
--再通过间接方式插入一行
SQL> insert into t values (3);
1 row created.
SQL> commit;
Commit complete.
SQL> select file#,block# from v$bh where objd=(select object_id from user_objects where object_name='T');
FILE# BLOCK#
---------- ----------
1 105344
1 105345
哈哈很奇怪吧,刚才通过直接路径插入了999行,只buufer中只有一个端头块,现在插入了一行就多读了一个块到buffer中,这是为什么呢?因为直接路径插入是从高水点以上分配空间,然后插入,而间接插入是先扫描端头块的freelist,发现在105345还有空间,就将其读到buffer中,然后在内存中修改,最后等DBWR写会磁盘
--再查询这个块的行分布,插入了999行已经多了3块,在前面这些块都没有被调入到buffer
SQL> select distinct dbms_rowid.rowid_block_number(rowid) from t;
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------------
105347
105348
105346
105345
--这个时候高水点已经上升
SQL> alter system dump datafile 1 block 105344;
System altered.
SQL> select tracefile from v$process where addr=(select paddr from v$session where sid=(select sid from v$mystat where rownum=1));
TRACEFILE
--------------------------------------------------------------------------------
/u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_17131.trc
[oracle@localhost ~]$ cat /u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_17131.trc | grep Highwater:
Highwater:: 0x00419b85 ext#: 0 blk#: 4 ext size: 7
SQL> select dbms_utility.data_block_address_block(to_number('00419b85','xxxxxxxx')) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(TO_NUMBER('00419B85','XXXXXXXX'))
-----------------------------------------------------------------------
105349
高水点上升到了105349。
二 直接加载和redo的关系。
SQL> create table t1 as select object_id,object_name from dba_objects;
Table created.
SQL> create table t2 as select object_id,object_name from dba_objects;
Table created.
--观察两种方式redo size
SQL> set autotrace trace stat;
SQL> insert into t1 select * from t1;
71942 rows created.
Statistics
----------------------------------------------------------
164 recursive calls
1964 db block gets
1127 consistent gets
342 physical reads
3004812 redo size
839 bytes sent via SQL*Net to client
787 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
71942 rows processed
SQL> insert /*+ append */ into t2 select * from t2;
71942 rows created.
Statistics
----------------------------------------------------------
167 recursive calls
379 db block gets
452 consistent gets
342 physical reads
2819012 redo size
823 bytes sent via SQL*Net to client
801 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
71942 rows processed
间接redo size:3004812
直接redo size: 2819012 相差比例很小
SQL> rollback;
Rollback complete.
--将t1,t2表改为nologging方式
SQL> alter table t1 nologging;
Table altered.
SQL> alter table t2 nologging;
Table altered.
SQL> insert /*+ append */ into t1 select * from t1;
143884 rows created.
Statistics
----------------------------------------------------------
0 recursive calls
691 db block gets
689 consistent gets
0 physical reads
1424 redo size
819 bytes sent via SQL*Net to client
801 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
143884 rows processed
SQL> insert into t2 select * from t2;
143884 rows created.
Statistics
----------------------------------------------------------
172 recursive calls
2739 db block gets
5460 consistent gets
0 physical reads
5931272 redo size
836 bytes sent via SQL*Net to client
787 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
143884 rows processed
直接redo size: 1424
间接redo size:5931272 相差很大
我们知道如果在创建表时默认为logging方式,上面结果显示了直接加载redo值有1424,实际上对于这种方式,undo的数据量产生也很少(下面实验会证明),因为在直接加载的数据并不会在回滚段中记录,这些记录位于高水位上,如果事务回滚,只需要保持高水位点不变就行了,如果提交高水位点则会上升,
根据上面结论说明了直接加载对于logging属性的表并不能提高执行效率,而对于nologging属性的表来说就有性能就有明显的区别,因为没有redo和undo的产生。这里要说明下上面有1424 redo size 是因为insert的时候需要空间的分配,需要修改字典表或者修改端头信息会产生少量的redo。
另外需要注意:对于这样的操作既然不会写redo就不会写归档,如果需恢复,需要立刻备份。
三 直接加载与undo
SQL> create table t as select rownum id from dba_objects;
Table created.
SQL> select file#,block# from v$bh where objd=(select object_id from user_objects where object_name='T');
FILE# BLOCK#
---------- ----------
1 105344
SQL> insert /*+ append */ into t select * from t;
71942 rows created.
SQL> select xidusn,xidslot,xidsqn,ubafil,ubablk,ubasqn from v$transaction;
XIDUSN XIDSLOT XIDSQN UBAFIL UBABLK UBASQN
---------- ---------- ---------- ---------- ---------- ----------
1 27 1139 0 0 0
从上面的显示结果可以看到,UBAFIL、UBABLK为0。也就是此事务并没有对应的回滚块,前面的实验已经证实了直接插入在是在高水位上分配空间,如果用户rollback保持高水位不变就可以了,如果commit提交高水位,这里就不做实验了,前面的实验已经可以充分说明了。
四 直接加载与index
SQL> select count(*) from t;
COUNT(*)
----------
2302144
SQL> create index idx_t on t(id);
Index created.
SQL> create index idx_t on t(id);
Index created.
SQL> insert /*+ append */ into t select * from t;
143884 rows created.
Statistics
----------------------------------------------------------
71 recursive calls
2923 db block gets
524 consistent gets
0 physical reads
6286380 redo size
824 bytes sent via SQL*Net to client
799 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
143884 rows processed
SQL> drop index idx_t;
Index dropped.
SQL> insert /*+ append */ into t select * from t;
143884 rows created.
Statistics
----------------------------------------------------------
171 recursive calls
223 db block gets
527 consistent gets
0 physical reads
1802256 redo size
826 bytes sent via SQL*Net to client
799 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
143884 rows processed
当表没有索引的时候使用直接插入redo size相差很大,所以建议针对大数据量操作的表使用直接插入时,可以考虑先将索引DSIABLE,创建完成之后再重新建立索引
总结
优点:对于大数据量的insert可以考虑使用直接加载的方式,加快插入速度
前提条件需要将表设置为nologging方式,禁用表上的索引,insert完成后再修改回来
缺点:
无法通过undo和归档恢复,额外增加了维护人员操作量,可能会造成一定的空间浪费。