位图索引(bitmap index)冲突引起的TX锁争用

B*Tree索引的叶节点以排序形式存储索引条目,每个索引条目指向各自的一个rowid。所以唯一键冲突之外,索引条目之间不发生争用。而位图索引的叶节点具有 “column值+start rowid+ end rowid+ bitmap值”的形式。即,一个叶节点管理大范围的rowid.每当表的行被修改时,对位图索引相应的列值,每次都要重新计算行所属叶节点的位图。因此,两个会话同时对相同的叶节点执行位图运算时,为保障顺序,应该获取TX锁。即,如果特定会话在exclusive模式获得TX锁后,执行了位图运算,但是还没有提交,则其他会话为了对之前的事务保障,以shared 模式获得TX锁而等待,所以等到位图运算结束为止。一个叶节点管理大范围的ROWID,所以可能出现大量TX锁争用。位图索引冲突引发TX锁争用时,则等待enq:TX-row lock contention事件。

下面做一个测试:

打开一个会话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: %';

注意:位图索引在位图的段级别上(而不是在行级别上)设置锁。如果对位图索引中的一列进行更新,行级锁将会出问题。


你可能感兴趣的:(位图索引(bitmap index)冲突引起的TX锁争用)