Fosdccf.blog.chinaunix.net
sdccf
全部博文(19283)
Linux酷软(214)
tmp(0)
PostgreSQL(93)
Solaris(383)
AIX(173)
SCOUNIX(575)
DB2(1005)
Shell(386)
C/C++(1187)
MySQL(1750)
Sybase(465)
Oracle(3695)
Informix(548)
HP-UX(0)
IBM AIX(2)
Sun Solaris(0)
BSD(1)
Linux(8597)
SCO UNIX(23)
2011年(1)
2009年(125)
2008年(19094)
2007年(63)
clifford
linky521
曾德标
fengzhan
leon_yu
mcuflowe
yt200902
guanyuji
GY123456
snow888
carlos94
丸喵喵
sean229
cxunix
可怜的猪
cqxc413
xzzgege
wb123456
分类: Oracle
2008-05-01 18:00:53
Wwj/scttsc Valenwon/cnoug 2008 Apri 7 昨天为了提高fact table装载数据效率(同事写的etl代码,效率达不到要求),对Oracle的插入机制做了一些学习。虽然最后提高效率的关键还是在于Java code上,但是对Oracle并发写入原理有了一定的了解。 Oracle插入数据的时候,首要做的工作就是寻找可使用的数据块(HW下面未满数据块,HW上面未格式化的数据块),如果没有则会增加extent。 Oracle以前版本(9i。。)使用freelist来管理可空闲块,为了保证并发时候数据的完整性,一个进程在修改freelist前,必须锁定它,这样必然就会产生竞争。优化办法是采用多个freelist,但是又得考虑在空间和效率上获得折衷的数量。由于已经过时,不在研究。 Oracle10g中,ASSM已经称为segment管理的默认配置,在ASSM中是采用位图来管理空闲块。一般是两级位图块,如果数据量很大,可能会出现三级(itpub版主做过试验)。关于位图块的概念,网上很多,不做讲解。 继续采用《Oracle如何计算consistent gets》一文中的测试表TEST1,不过删除掉一些记录,构造一些没有满的块。 找出段头 SQL> SELECT s.header_file,s.header_block FROM dba_segments s WHERE s.segment_name = 'TEST1'; HEADER_FILE HEADER_BLOCK ----------- ------------ 5 11 dump出块内容 SQL> alter system dump datafile 5 block 11; 省略了其它内容,直接看感兴趣的部分 。。。 Second Level Bitmap block DBAs ――二级位图块的地址 -------------------------------------------------------- DBA 1: 0x0140000a -唯一一个二级位图块 。。。 0x0140000a 只需要后半部分,a 转换为10机制就是10,那么继续dump block10 SQL> alter system dump datafile 5 block 10; 一个二级位图块的内容节略如下: Dump of Second Level Bitmap Block number: 5 nfree: 2 ffree: 0 pdba: 0x0140000b Inc #: 1 Objd: 51344 opcode:0 xid: L1 Ranges : -------------------------------------------------------- 0x01400009 Free: 5 Inst: 1 转换为10进制为 9 0x01400ce9 Free: 1 Inst: 1 转换为10进制为 3305 0x01400cf9 Free: 1 Inst: 1 0x014000c1 Free: 1 Inst: 1 0x014000d9 Free: 5 Inst: 1 转换为10进制为 217 -------------------------------------------------------- End dump data blocks tsn: 6 file#: 5 minblk 10 maxblk 10 继续dump 9 和3305 SQL> alter system dump datafile 5 block 9; SQL> alter system dump datafile 5 block 3305; Block 9的节选内容如下: -------------------------------------------------------- DBA Ranges : -------------------------------------------------------- 0x01400009 Length: 8 Offset: 0 开始地址 0x01400ce1 Length: 8 Offset: 8 结束地址 0:Metadata 1:Metadata 2:Metadata 3:75-100% free 4:75-100% free 5:75-100% free 6:75-100% free 7:75-100% free 8:75-100% free 9:75-100% free 10:75-100% free 11:0-25% free 12:FULL 13:FULL 14:FULL 15:FULL -------------------------------------------------------- 这里可以清晰的看出,第0 1 2是元数据块,实际就是我们dump 9 10 11块,4~11块都有空闲空间,那是因为我在试验开始删除部分数据。 Block 3305的节选内容如下: -------------------------------------------------------- DBA Ranges : -------------------------------------------------------- 0x01400ce9 Length: 8 Offset: 0 0x01400cf1 Length: 8 Offset: 8 0:Metadata 1:FULL 2:FULL 3:FULL 4:FULL 5:FULL 6:FULL 7:FULL 8:FULL 9:FULL 10:FULL 11:FULL 12:FULL 13:FULL 14:FULL 15:FULL 这个位图可以看出,除了它本身是metadata以外,其它它管理的块都是转满了数据的。 通过这些信息,对Oracle如果管理块就有一定的了解了。 那么Oracle是如何在并发插入的时候分配块呢? 为了观察数据的情况,我建了一个新出测试表: create table TEST4 ( C1 CHAR(2000) ) 在PLSQL dev里面测试,插入7行数据1,2,3,4,5,6,7 SQL> select dbms_rowid.rowid_block_number(t.rowid) from test4 t order by c1; DBMS_ROWID.ROWID_BLOCK_NUMBER(T.ROWID) -------------------------------------- 236 236 236 237 237 237 238 这个时候Oracle是采用挨着写数据块,写满一个换下一个的办法。删除数据。。。 模拟并发写入,我打开了7个SQL windows。我笔记本的测试库,是dedicate模式,oracle为这个7个windows分配了7个process(7个session)。 我分别插入1,2,3,4,5,6,7,注意都不要提交。 插入完毕,分别提交。 观察数据分布: SQL> select dbms_rowid.rowid_block_number(t.rowid) from test4 t order by c1; DBMS_ROWID.ROWID_BLOCK_NUMBER(T.ROWID) -------------------------------------- 236 237 238 239 239 240 236 在这个extents中,8个块,有5个可用写入数据块。236~240。 SQL> SELECT s.block_id,s.blocks FROM dba_extents s WHERE s.segment_name = 'TEST4'; BLOCK_ID BLOCKS ---------- ---------- 233 8 为了获得更好的并发性能,Oracle显而易见的采用了尽量为每个process分配不同的块。 为什么说尽量呢?如果所有的空闲块都分配完,那么就还是会吧已经分配完的块,再次分配给新来的process,这也是结果里面,239和236块多次使用的原因。 Oracle为什么不为每个process分配独立的块,这样虽然达成了最大并发,却可能严重浪费空间,所以这又是一个性能和空间的折衷。 Oracle是怎么选择使用那个块呢?应该是先锁定一个位图块,再分配,由于存在多个二级位图块(本例只有一个),所以提高了并发,再锁定,并在位图块里面分配不同的块。但这个算法,我不清楚。 Oracle“尽量”为每个process分配独立的块,已经是很大的进步了,那么如果多个process共用一个块,情况又是怎么样的呢? 这个时候,我们的研究对象,有变成了Data buffer。 当Oracle某个process 找到一个可用块的时候,做的第一件事情,并不是要去写它,而且把它读取到Data buffer中。Oracle是通过各种“锁”,来保证对资源的并发访问。(说白了,再快的并行,其实在某一局部也是串行,只不过这个串行相当的快罢了,在多线程编程中经常遇到,实际应用中几乎没有全盘的并发,只是把效率低下的部分并发来配合效率高的部分。。。) Oracle通过一个Hash链来维护databuffer header,每一个Hash bucket里面存储着N个databuffer header,Hash key 是相关的data block address和block class。 Oracle用户进程找到某一个块,准备写入的时候,会在相应的databuffer header加锁,也就是我们常说的latch,避免同时被其它进程同时访问块,加上锁以后,用户进程会立刻在块里面更新SCN,打上事务标记,并且锁定要使用的行,释放latch。这个时候,下一个用户进程才得以重新重复以上动作。虽然上面的步骤貌似很多,但是实际是在相当的快的。 通过使用latch,使得用户进程可用并发的使用块,各自处理自己锁定的行。 latch则是串行。 算了,Buffer cache重新再另一文章总结。
小结:总得来说,10g里面,Oracle的并发插入已经比较高效,但是也不排除在高度并发并且采用Dedicate的环境中,会出现性能问题(latch竞争)。但在Mts下,一般连接池问题不大。
上一篇:深入学习Oracle的Hash join
下一篇: PL/SQL补疑之Collections and Records <2>
登录 注册