下面做一个测试:
打开一个会话1:
SQL> select sid from v$mystat where rownum<2;
SID
----------
148
SQL> create table tx_b(name1 char(10),name2 char(10),name3 char(10));
表已创建。
SQL> create bitmap index tx_b_idx on tx_b(name1,name2,name3);
索引已创建。
SQL> insert into tx_b values('a','b','c');
已创建 1 行。
新打开一个会话2:
SQL> select sid from v$mystat where rownum<2;
SID
----------
141
SQL> insert into tx_b values('a','b','c');
一直处于等待状态
再打开一个会话3:
SQL> select * from v$lock where type='TX';
ADDR KADDR SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
6C26DA0C 6C26DB20 141 TX 327684 3452 6 0 111 0
6CA63958 6CA6396C 141 TX 262181 14897 0 4 111 0
6C2953FC 6C295510 148 TX 262181 14897 6 0 216 1
这个结果与唯一键冲突引起的等待现象完全相同。只通过等待现象,不能区别唯一键冲突和位图索引冲突之间的差异,只有在同时考虑到创建索引的准确信息和sql语句,才能掌握准确的原因。
索引叶节点(leaf node)上发生分割时,相关的等待时间是enq:TX-index contention
B*Tree索引在添加数据的过程中,如果叶节点已满就会进行分割(split),以此到大平衡,会话A在exclusive模式已获得TX锁的情况下,执行分割的过程中,会话B正要修改叶节点时,会话B为了以shared模式获得会话A拥有的TX锁只好等待,在此期间会发生enq:TX-index contention等待事件。
一般情况下enq:TX-index contention等待不会发生,它主要是在多个会话对已有索引的表执行较多量的DML时发生。这个等待现象虽然不经常发生,但创建的数量多,组成索引的列值大而指针叶节点的块频繁被分割时,成为性能下降的原因,特别是使用sequence等方式生成值的列在创建索引时,一直出现只在最后的叶节点添加值的现象,所以可能经常发生索引分割。这是以排序形式保持叶节点的B*Tree 索引属性引起的,因此多个会话将大量的数据执行insert 时,与buffer busy waits 等待一起发生enq:TX-index contention等待。
减少索引分割引发的争用的基本方法,就是阻止在相同的叶节点块里集中添加数据的现象,例如可以应用partitioning方法进行物理分散,或是修改该组成索引的列的顺序而自然分散等方法。单若存在以特定键为基准排序这样的约束条件,就无法使用此方法。具有代表性的情况是利用sequence赋予主键值,利用/*INDEX_DESC*/ 之类的提示,对此索引以排序的方式执行扫描数据查询。通过这种方式使用索引时,必须保障相应键为基准排序,所以不能在索引上应用partitioning或修改该索引的列顺序。
另一种方法是将索引的块设定的较大。使用较大的块时,一个块上的条目数量多,因此较少发生分割。但是若块大小增加,就可能引发buffer lock 争用引起的buffer busy waits 等待现象,所以要谨慎使用。
请注意一点,修改没有创建索引的表过程中,有时能发生enq:TX-index contention等待。表里有lob列时,就会从内部创建对于LOB数据的索引(称为LOB索引)因此多个会话同时修改LOB数据时会发生索引争用。
)其他情况时,相关的等待时间是enq:TX-contention
***分布式事务(distributed transaction)环境下,通过prepared transaction读取已获得锁的行时,知道事务结束为止,为了一shared 模式获取TX锁而需要等待。
***将FLM以段空间管理方法使用时,想要分配TFL(transaction free list)的进程无法分配到TFL时,为了一shared 模式获得已经占有TFL的事务的TX锁,需要等待。
***回滚段头的事务表上西药分配新的slot时,应该以exclusive模式获得TX锁。
enq:UL-contention PL/SQL lock Timer
使用DBMS_LOCK程序包,可以对任意假想的资源挂起锁,如果是因为DML发生的锁时,虽然必须需要物理资源(表,事务,段等),但使用DBMS_LOCK程序包没有这种限制,使用DBMS_LOCK程序包获取锁称为UL(Userdefined Lock)锁,为了获得UL锁 而等待的会话,将发生enq:UL-contention等待事件。
利用DBMS_LOCK.REQUEST函数可以将UL锁以Exclusive模式获得,利用DBMS_LOCK.RELEASE函数,可以释放锁。记住UL锁的释放只能在拥有锁的会话上实现,若特定会话因长时间拥有UL锁,而引发并发性问题,则除了强制结束会话之外没有其他方法。使用DBMS_LOCK.REQUEST函数时,尽量使用RELEASE_ON_COMMIT选项,以避免多余的拥有锁,使用这个选项若发生提交或回滚,则自动释放该事务所拥有的UL锁。
另外,还有一个与DBMS_LOCK程序包相关的等待事件,利用DBMS_LOCK.SLEEP Procedure 暂时中断事务时,则该进程等待PL/SQL lock timer事件。
PL/SQL lock timer 等待事件不会引起性能的问题。如果该等待时间比预期还要长,就应该重新检查应用程序的实现逻辑是否有问题。
以下查询,可以查询出在位图索引上的等待事件:
SELECT sid, event, wait_time_micro / 1000 time_ms, blocking_session, object_type || ': ' || object_name object, sql_text FROM v$session s LEFT OUTER JOIN v$sql USING (sql_id) LEFT OUTER JOIN dba_objects ON (object_id = row_wait_obj#) WHERE event LIKE 'enq: %';
注意:位图索引在位图的段级别上(而不是在行级别上)设置锁。如果对位图索引中的一列进行更新,行级锁将会出问题。