Oracle里面的写入数据方式分为2种,一个是传统方式写入(conventional insert),另外一个是直接路径写入(direct path insert)。
传统方式写入: Oracle会重用表里面空闲空间,并且写入会先写入到buffer cache。
直接路径写入:Oracle不重用表里面的空闲空间,直接写入到新分配的块,并且数据直接写入到data file,不写入到buffer cache。效率更高。如果表的并行度不为1,那么直接路径写入是默认的写入行为(需要开启会话并行dml)。直接路径写入为元数据(例如空间扩展引起的数据字典数据变化)变更产生REDO, UNDO。而数据变更,redo,undo生成情况如下:
Ø 不为数据写入生成undo
Ø 如果数据库为非归档或者没有开启force logging,那么直接路径写入时,不为数据写入产生redo,这时与表是否设置nologging属性无关。
Ø 如果数据库为归档,但是没有开启force logging,logging的表会产生redo,nologging的表不会产生redo。
Ø 如果数据库为归档并且开启了force logging,数据写入会产生redo,无论是否设置logging,nologging。
直接路径写入的一些限制:
Ø 在一个事务里面,可以执行多次直接路径写入,一旦该事物并行修改了表,分区或者索引,当前会话将无法再次方位该对象。例如会话A在表T上进行直接路径写入后,会话A无法再次访问表T,但是会话B可以查询表T,此时如果会话A执行一个传统路径写入,会被阻塞,等待事件为:enq: TM – contention。
Ø 目标表不能是cluster,不能含有object类型字段,不能含有外键约束,触发器。
Ø 对于目标表是IOT表,也有一些限制。
Ø 分布式写入无法进行直接路径写入。
APPEND,PARALLE和直接路径写入关系:
Append默认也是采用直接路径写入,不要求session enable parallel。
Parallel 要求会话级别enable parallel,否则将无法进行直接路径写入。
测试如下:
- SQL> select log_mode, force_logging from v$database;
- LOG_MODE FORCE_
- ------------------------ ------
- ARCHIVELOG NO
- SQL> select * from v$version;
- BANNER
- -------------------------------------------------------------------------------------------------------------------
- Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
- PL/SQL Release 11.2.0.3.0 - Production
- CORE 11.2.0.3.0 Production
- TNS for Linux: Version 11.2.0.3.0 - Production
- NLSRTL Version 11.2.0.3.0 - Production
- ===scott测试用户,构造测试数据源和数据表
- SQL> conn scott/tiger;
- Connected.
- SQL> create table t_data_pool as select * from dba_objects;
- Table created.
- SQL> create table t_direct_test as select * from t_data_pool where 1=2;
- Table created.
- SQL>
1、测试REDO生成的量,以及直接路径阻塞传统写入的场景
- ==场景1,Redo的量
- SQL> set autot on;
- SQL> insert into t_direct_test select * from t_data_pool;
- 75696 rows created.
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 2724272089
- ------------------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- ------------------------------------------------------------------------------------------
- | 0 | INSERT STATEMENT | | 76299 | 15M| 216 (1)| 00:00:03 |
- | 1 | LOAD TABLE CONVENTIONAL | T_DIRECT_TEST | | | | |
- | 2 | TABLE ACCESS FULL | T_DATA_POOL | 76299 | 15M| 216 (1)| 00:00:03 |
- ------------------------------------------------------------------------------------------
- Note
- -----
- - dynamic sampling used for this statement (level=2)
- Statistics
- ----------------------------------------------------------
- 266 recursive calls
- 10388 db block gets
- 3228 consistent gets
- 784 physical reads
- 8845208 redo size ==> redo 量
- 837 bytes sent via SQL*Net to client
- 807 bytes received via SQL*Net from client
- 3 SQL*Net roundtrips to/from client
- 2 sorts (memory)
- 0 sorts (disk)
- 75696 rows processed
- SQL> commit;
- Commit complete.
- ==使用append, logging时,redo量测试如下:
- SQL> insert /*+append*/ into t_direct_test select * from t_data_pool;
- 75696 rows created.
- Execution Plan
- ----------------------------------------------------------
- ERROR:
- ORA-12838: cannot read/modify an object after modifying it in parallel
- SP2-0612: Error generating AUTOTRACE EXPLAIN report
- Statistics
- ----------------------------------------------------------
- 94 recursive calls
- 1397 db block gets
- 1204 consistent gets
- 0 physical reads
- 8890936 redo size ==>redo 量
- 823 bytes sent via SQL*Net to client
- 819 bytes received via SQL*Net from client
- 3 SQL*Net roundtrips to/from client
- 1 sorts (memory)
- 0 sorts (disk)
- 75696 rows processed
- SQL> commit;
- Commit complete
- ==nologging append量,明显很少了
- SQL> alter table t_direct_test nologging;
- Table altered.
- SQL> insert /*+append*/ into t_direct_test select * from t_data_pool;
- 75696 rows created.
- Execution Plan
- ----------------------------------------------------------
- ERROR:
- ORA-12838: cannot read/modify an object after modifying it in parallel
- SP2-0612: Error generating AUTOTRACE EXPLAIN report
- Statistics
- ----------------------------------------------------------
- 139 recursive calls
- 1361 db block gets
- 1229 consistent gets
- 0 physical reads
- 20904 redo size ==>Redo 量
- 824 bytes sent via SQL*Net to client
- 819 bytes received via SQL*Net from client
- 3 SQL*Net roundtrips to/from client
- 7 sorts (memory)
- 0 sorts (disk)
- 75696 rows processed
- SQL> select count(1) from t_direct_test;
- select count(1) from t_direct_test
- *
- ERROR at line 1:
- ORA-12838: cannot read/modify an object after modifying it in parallel
- ==此时不提交,当前事务是无法访问到该对象的,其他session可以。
新开会话B,
- oracle@ora11gr2 ~]$sqlplus scott/tiger
- SQL*Plus: Release 11.2.0.3.0 Production on Wed Jun 11 12:30:31 2014
- Copyright (c) 1982, 2011, Oracle. All rights reserved.
- Connected to:
- Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
- With the Partitioning, OLAP, Data Mining and Real Application Testing options
- SQL> select count(1) from t_direct_test;
- COUNT(1)
- ----------
- 151392
- SQL> insert into t_direct_test select * from t_data_pool; ==会被阻塞
新开会话C,查看锁的情况:
- 当前会话申请0号TM锁被直接路径写入会话阻塞,直接路径写入会话持有Exclusive(6) TM锁。
- col resource format a20
- col sid format 9999
- col request for a15
- Select Type || '-' || Id1 || '-' || Id2 "resource",
- Sid,
- Decode(Lmode,
- 0,
- 'None',
- 1,
- 'Null',
- 2,
- 'Row share',
- 3,
- 'Row Exclusive',
- 4,
- 'Share',
- 5,
- 'Share Row Exclusive',
- 6,
- 'Exlusive') Lock_Type,
- Decode(Request,
- 0,
- 'None',
- 1,
- 'Null',
- 2,
- 'Row share',
- 3,
- 'Row Exclusive',
- 4,
- 'Share',
- 5,
- 'Share Row Exclusive',
- 6,
- 'Exlusive') Request,
- Ctime,
- Block
- From V$lock
- Where Type In ('TX', 'TM')
- 37 Order By "resource", Ctime Desc;
- resource SID LOCK_TYPE REQUEST CTIME BLOCK
- -------------------- ----- -------------------------------------- --------------- ---------- ----------
- TM-5124-0 44 Row Exclusive None 905 0
- TM-78585-0 29 Row Exclusive None 2912 0
- TM-78587-0 44 Exlusive None 906 1 ====> TM上持有6号锁
- TM-78587-0 58 None Row Exclusive 380 0 ====> TM上被阻塞
- TX-262166-8704 44 Exlusive None 906 0
- TX-458782-8781 29 Exlusive None 2912 0
- 6 rows selected.
- SQL>
- Sid为44的正在执行直接路径插入,当时还没有提交,而58号 会话在执行一个传统路径写入
2、测试append,parallel的默认插入方式
上面的测试已经表名,在表默认的并行度为1的情况下,直接append是会选择直接路径写入方式。下面现将表t_direct_test的并行度设置为4.
- SQL> conn scott/tiger
- Connected.
- SQL> col owner for a10
- SQL> col degree for 999
- SQL> col table_name for a20
- SQL> select owner, table_name, Degree from dba_tables Where table_name = Upper('t_direct_test');
- OWNER TABLE_NAME DEGREE
- ---------- -------------------- --------------------------------------------------------------------------------
- SCOTT T_DIRECT_TEST 1
- SQL> alter table t_direct_test parallel 4;
- Table altered.
- SQL> select owner, table_name, Degree from dba_tables Where table_name = Upper('t_direct_test');
- OWNER TABLE_NAME DEGREE
- ---------- -------------------- --------------------------------------------------------------------------------
- SCOTT T_DIRECT_TEST 4
- SQL> insert into t_direct_test select * from t_data_pool;
- 75696 rows created.
- SQL> select count(1) from t_direct_test; ==>没有报错,即表明没有用到直接路径写入
- COUNT(1)
- ----------
- 378480
- SQL> commit;
- Commit complete.
- SQL> alter session enable parallel dml;
- Session altered.
- SQL> insert into t_direct_test select * from t_data_pool;
- 75696 rows created.
- SQL> select count(1) from t_direct_test;
- ==报错,说明在会话开启允许并行dml的情况下,会直接使用直接路径写入方式。
- select count(1) from t_direct_test
- *
- ERROR at line 1:
- ORA-12838: cannot read/modify an object after modifying it in parallel
- SQL>