您的位置:首页 > 数据库 > Oracle

ORACLE等待事件:enq: TX - row lock contention

2016-09-19 23:26 549 查看
enq:TX-rowlockcontention等待事件,这个是数据库里面一个比较常见的等待事件。enq是enqueue的缩写,它是一种保护共享资源的锁定机制,一个排队机制,先进先出(FIFO)。enq:TX-rowlockcontention等待事件,OACLE将其归类为application级别的等待事件。有些场景是因为应用逻辑设计不合理造成的。下面我们看看enq:TX-rowlockcontention的英文介绍:ThiswaitindicatestimespentwaitingforaTXlock,typicallyduetowaitingtogainaccesstoarowinatablethatiscurrentlylockedbythattransaction.TheTXlockwaitedonis"TX-P2RAW-P3RAW"andtheobject/rowthattriggeredthewaitcanusuallybefoundintheROW_WAIT_*columnsofV$SESSIONforthewaitingsession.ATXlockisacquiredwhenatransactioninitiatesitsfirstchangeandishelduntilthetransactiondoesaCOMMITorROLLBACK.Itisusedmainlyasaqueuingmechanismsothatothersessionscanwaitforthetransactiontocomplete.Thelockname(ID1andID2)oftheTXlockreflectthetransactionIDoftheactivetransaction.下面我们模拟一下enq:TX-rowlockcontention等待事件出现的场景,希望能对这个等待事件有较深的理解,主要参考了官方文档ID62354.11WaitsduetoRowbeinglockedbyanactiveTransaction这个是因为不同的session同时更新或删除同一个记录。例如,会话1持有rowlevellock,会话2在等待这个锁释放。准备测试环境和数据

SQL>createtabletest
2(idnumber(10),
3namevarchar2(16)
4);
Tablecreated.
SQL>insertintotest
2values(1001,'kk');
1rowcreated.
SQL>insertintotest
values(1002,'tttt')
1rowcreated.
SQL>commit;
Commitcomplete.
SQL>


会话1(会话ID为75)更新某一行

SQL>selectsidfromv$mystatwhererownum=1;
SID
----------
75
SQL>updatetestsetname='ken'whereid=1001;
1rowupdated.
SQL>

会话2(会话ID为200)也更新这一行(删除亦可)

SQL>selectsidfromv$mystatwhererownum=1;
SID
----------
200
SQL>updatetestsetname='kerry'whereid=1001;--一直被阻塞

在会话3中查看对应的会话、锁以及等待相关信息,这些SQL各

SQL>coltypefora32;
SQL>SELECTsid,
2type,
3id1,
4id2,
5lmode,
6request
7FROMv$lock
8WHEREtype='TX';
SIDTYPEID1ID2LMODEREQUEST
----------------------------------------------------------------------------------
200TX655385236106
75TX655385236160
SQL>COLeventFORa36;
SQL>SELECTsid,
2Chr(Bitand(p1,-16777216)/16777215)
3||Chr(Bitand(p1,16711680)/65535)"name",
4(Bitand(p1,65535))"mode",
5event,
6sql_id,
7final_blocking_session
8FROMv$session
9WHEREeventLIKE'enq%';
SIDnamemodeEVENTSQL_IDFINAL_BLOCKING_SESSION
---------------------------------------------------------------------------------------------------
200TX6enq:TX-rowlockcontentioncz4tvs78skhus75
SQL>COLwait_classFORA32;
SQL>SELECTinst_id,
2blocking_session,
3sid,
4serial#,
5wait_class,
6seconds_in_wait
7FROMgv$session
8WHEREblocking_sessionISNOTNULL
9ORDERBYblocking_session;
INST_IDBLOCKING_SESSIONSIDSERIAL#WAIT_CLASSSECONDS_IN_WAIT
---------------------------------------------------------------------------------------------
17520012230Application179
SQL>COLTXFORA24;
SQL>SELECT
2sid,seq#,state,seconds_in_wait,
3'TX-'||lpad(ltrim(p2raw,'0'),8,'0')||'-'||lpad(ltrim(p3raw,'0'),8,'0')TX,
4trunc(p2/65536)XIDUSN,
5trunc(mod(p2,65536))XIDSLOT,
6p3XIDSQN
7FROMv$session_wait
8WHEREevent='enq:TX-rowlockcontention';
SIDSEQ#STATESECONDS_IN_WAITTXXIDUSNXIDSLOTXIDSQN
------------------------------------------------------------------------------------------------------------
20046WAITING203TX-000A0019-0000093910252361
SQL>coleventfora36
SQL>colusernamefora10
SQL>colsql_fulltextfora80
SQL>SELECTg.inst_id,
2g.sid,
3g.serial#,
4g.event,
5g.username,
6g.sql_hash_value,
7s.sql_fulltext
8FROMgv$sessiong,
9v$sqls
10WHEREg.wait_class='Application'
11ANDg.sql_hash_value=s.hash_value;
INST_IDSIDSERIAL#EVENTUSERNAMESQL_HASH_VALUESQL_FULLTEXT
--------------------------------------------------------------------------------------------------------------------
120012230enq:TX-rowlockcontentionTEST3515433816updatetestsetname='kerry'whereid=1001
SQL>coltypefora12;
SQL>select/*+rule*/
2inst_id,
3decode(request,0,'holder','waiter')role,
4sid,
5type,
6request,
7lmode,
8block,
9ctime,
10id1,
11id2
12fromgv$lock
13where(id1,id2,type)in
14(selectid1,id2,typefromgv$lockwhererequest>0)
15orderbyctimedesc,role;
INST_IDROLESIDTYPEREQUESTLMODEBLOCKCTIMEID1ID2
--------------------------------------------------------------------------------------------------
1holder75TX0612556553852361
1waiter200TX6002456553852361




此时只能等待持有锁的会话commit或者rollback。通常为会话1在某行上执行update/delete未提交,会话2对同一行数据进行update/delete,或其它原因(例如SQL性能差)造成的锁释放速度缓慢或网络问题,都会造成后续的会话进入队列等待。

2WaitsduetoUniqueorPrimaryKeyConstraintEnforcement
表上存在主键或唯一索引,会话1插入一个值(未提交),会话2同时或随后也插入同样的值;会话提交后1,enq:TX-rowlockcontention消失。

SQL>droptabletestpurge;
Tabledropped.
SQL>createtabletest
2(
3idnumber(10),
4namevarchar(16),
5constraintpk_testprimarykey(id)
6);
Tablecreated.


会话1(会话ID为8)

SQL>insertintotestvalues(1000,'kerry');
1rowcreated.

会话2(会话ID为14)

SQL>insertintotestvalues(1000,'jimmy');

会话3在会话3中查看对应的会话、锁、以及等待信息

SQL>coltypefora32;
SQL>SELECTsid,
2type,
3id1,
4id2,
5lmode,
6request
7FROMv$lock
8WHEREtype='TX';
SIDTYPEID1ID2LMODEREQUEST
----------------------------------------------------------------------------------
14TX589835221304
14TX393232216060
8TX589835221360
SQL>coleventfora40
SQL>colusernamefora10
SQL>colsql_fulltextfora80
SQL>SELECTg.inst_id,
2g.sid,
3g.serial#,
4g.event,
5g.username,
6g.sql_hash_value,
7s.sql_fulltext
8FROMgv$sessiong,
9v$sqls
10WHEREg.wait_class='Application'
11ANDg.sql_hash_value=s.hash_value;
INST_IDSIDSERIAL#EVENTUSERNAMESQL_HASH_VALUESQL_FULLTEXT
---------------------------------------------------------------------------------------------------------
11413392enq:TX-rowlockcontentionTEST874051616insertintotestvalues(1000,'jimmy')
SQL>coltypefora12;
SQL>select/*+rule*/
2inst_id,
3decode(request,0,'holder','waiter')role,
4sid,
5type,
6request,
7lmode,
8block,
9ctime,
10id1,
11id2
12fromgv$lock
13where(id1,id2,type)in
14(selectid1,id2,typefromgv$lockwhererequest>0)
15orderbyctimedesc,role;
INST_IDROLESIDTYPEREQUESTLMODEBLOCKCTIMEID1ID2
--------------------------------------------------------------------------------------------------------
1holder8TX061925898352213
1waiter14TX400705898352213

会话1(会话ID为8)提交事务后

SQL>insertintotestvalues(1000,'kerry');
1rowcreated.
SQL>commit;

会话2(会话ID为14)遇到ORA-00001错误提示

SQL>insertintotestvalues(1000,'jimmy');
insertintotestvalues(1000,'jimmy')
*
ERRORatline1:
ORA-00001:uniqueconstraint(TEST.PK_TEST)violated


这个在ORACLE10g以及以上版本都无法测试(Oracle9i可以测试),因为ORACLE10g中,对于单个数据块,Oracle缺省最大支持255个并发,MAXTRANS参数在ORACLE10g以及以上版本被废弃了,即使你使用下面SQL指定了maxtrans为1,但是你查看表的定义,你会发现maxtrans依然为255。

SQL>droptabletestpurge;
Tabledropped.
SQL>createtabletest
2(
3idnumber(10),
4namevarchar(16)
5)initrans1maxtrans1;
Tablecreated.

所以这个场景只会发生在ORACLE9i的版本中或是并发非常高的系统当中。
WaitsduetorowsbeingcoveredbythesameBITMAPindexfragment
这个源于位图索引的特性,更新位图索引的一个键值,会指向多行记录,所以更新一行就会把该键值指向的所有行锁定

SQL>createtableemployee
2(
3employee_idnumber(10),
4employee_namenvarchar2(24),
5sexvarchar2(6)
6);
Tablecreated.
SQL>createbitmapindexidx_employee_bitmaponemployee(sex);
Indexcreated.
SQL>insertintoemployee
2select1000,'kerry','female'fromdualunionall
3select1001,'jimmy','female'fromdual;
2rowscreated.
SQL>commit;
Commitcomplete.
SQL>
会话1:
SQL>updateemployeesetsex='male'whereemployee_id=1000;
1rowupdated.
会话2:
SQL>updateemployeesetsex='male'whereemployee_id=1001;
会话3:
SQL>coltypefora32;
SQL>SELECTsid,
2type,
3id1,
4id2,
5lmode,
6request
7FROMv$lock
8WHEREtype='TX';
SIDTYPEID1ID2LMODEREQUEST
----------------------------------------------------------------------------------
14TX589836221104
14TX131096220460
8TX589836221160
SQL>coleventfora40
SQL>colusernamefora10
SQL>colsql_fulltextfora80
SQL>SELECTg.inst_id,
2g.sid,
3g.serial#,
4g.event,
5g.username,
6g.sql_hash_value,
7s.sql_fulltext
8FROMgv$sessiong,
9v$sqls
10WHEREg.wait_class='Application'
11ANDg.sql_hash_value=s.hash_value;
INST_IDSIDSERIAL#EVENTUSERNAMESQL_HASH_VALUESQL_FULLTEXT
---------------------------------------------------------------------------------------------------------------------
11413392enq:TX-rowlockcontentionEST2418349426updateemployeesetsex='male'whereemployee_id=1001
SQL>coltypefora12;
SQL>select/*+rule*/
2inst_id,
3decode(request,0,'holder','waiter')role,
4sid,
5type,
6request,
7lmode,
8block,
9ctime,
10id1,
11id2
12fromgv$lock
13where(id1,id2,type)in
14(selectid1,id2,typefromgv$lockwhererequest>0)
15orderbyctimedesc,role;
INST_IDROLESIDTYPEREQUESTLMODEBLOCKCTIMEID1ID2
--------------------------------------------------------------------------------------------------------
1holder8TX0611165898362211
1waiter14TX400765898362211
SQL>

其它场景
ThereareotherwaitscenarioswhichcanresultinaSHAREmodewaitforaTXlockbutthesearerarecomparedtotheexamplesgivenabove.
Example:
IfasessionwantstoreadarowlockedbyatransactioninaPREPAREDstatethenitwillwaitontherelevantTXlockinSHAREmode(REQUEST=4).AsaPREPAREDtransactionshouldCOMMIT,ROLLBACKorgotoanin-doubtstateverysoonafterthepreparethisisnotgenerallynoticeable.
例如,表存在主外键读情况,主表不提交,子表那么必须进行等待.
初始化测试表

SQL>createtableemployee(employee_idnumber,employee_namevarchar(12),depart_idnumber);
Tablecreated.
SQL>createtabledepartment(depart_idnumberprimarykey,depart_namevarchar2(24));
Tablecreated.

会话1:

SQL>selectsidfromv$mystatwhererownum=1;
SID
----------
75
SQL>insertintodepartmentvalues(1000,'sales');
1rowcreated.

会话2:

SQL>selectsidfromv$mystatwhererownum=1;
SID
----------
200
SQL>insertintoemployeevalues(1024,'kerry',1000);--一直挂起,直到会话1提交


会话3

SQL>showuser;
USERis"SYS"
SQL>selectsidfromv$mystatwhererownum=1;
SID
----------
73
SQL>coltypefora12;
SQL>select/*+rule*/
2inst_id,
3decode(request,0,'holder','waiter')role,
4sid,
5type,
6request,
7lmode,
8block,
9ctime,
10id1,
11id2
12fromgv$lock
13where(id1,id2,type)in
14(selectid1,id2,typefromgv$lockwhererequest>0)
15orderbyctimedesc,role;
INST_IDROLESIDTYPEREQUESTLMODEBLOCKCTIMEID1ID2
--------------------------------------------------------------------------------------------------
1holder75TX0611974587582371
1waiter200TX400974587582371
SQL>COLTXFORA24;
SQL>SELECT
2sid,seq#,state,seconds_in_wait,
3'TX-'||lpad(ltrim(p2raw,'0'),8,'0')||'-'||lpad(ltrim(p3raw,'0'),8,'0')TX,
4trunc(p2/65536)XIDUSN,
5trunc(mod(p2,65536))XIDSLOT,
6p3XIDSQN
7FROMv$session_wait
8WHEREevent='enq:TX-rowlockcontention';
SIDSEQ#STATESECONDS_IN_WAITTXXIDUSNXIDSLOTXIDSQN
------------------------------------------------------------------------------------------------------------
200108WAITING145TX-00070006-00000943762371
SQL>


另外遇到enq:TX-rowlockcontention等待事件,单实例与RAC是否有所区别呢,如果是RAC,需要注意识别实例,否则很容易误杀其它会话?如果你查到了blocker,是不是应该直接kill掉呢?这个必须要先征询客户的意见,确认之后才可以杀掉。不能因为外在压力和自己的急躁而擅自Kill会话。
在WAITEVENT:"enq:TX-rowlockcontention"ReferenceNote(文档ID1966048.1)中,也有一些比较有意思的SQL,可以参考一下

SQL>SELECTrow_wait_obj#,row_wait_file#,row_wait_block#,row_wait_row#
2FROMv$session
3WHEREevent='enq:TX-rowlockcontention'
4ANDstate='WAITING';
ROW_WAIT_OBJ#ROW_WAIT_FILE#ROW_WAIT_BLOCK#ROW_WAIT_ROW#
-------------------------------------------------------
7538942110
SQL>setlinesize240;
SQL>selectowner,object_namefromdba_objects
2whereobject_id=75389;
OWNEROBJECT_NAME
------------------------------------------------
TESTTEST
SQL>SELECT
2sid,seq#,state,seconds_in_wait,
3'TX-'||lpad(ltrim(p2raw,'0'),8,'0')||'-'||lpad(ltrim(p3raw,'0'),8,'0')TX,
4trunc(p2/65536)XIDUSN,
5trunc(mod(p2,65536))XIDSLOT,
6p3XIDSQN
7FROMv$session_wait
8WHEREevent='enq:TX-rowlockcontention'
9;
SIDSEQ#SECONDS_IN_WAITTXXIDUSNXIDSLOTXIDSQN
-----------------------------------------------------------------------------------------------------
13727WAITING245TX-00040012-000007E64182022

TX-rowlockcontention的一些场景这篇文章里面介绍了出现enq:TX-rowlockcontention等待的案例场景,网络问题、执行计划问题、应用问题等。在我遇到的实际案例当中,网络问题造成的'enq:TX-rowlockcontention'较多,因为现在大多数是无线网络,有些应用程序出现问题或网络出现问题过后,导致数据库中的进程依然在,但是对于的UPDATE等DML操作没有及时提交。从而出现较严重的enq:TX-rowlockcontention
诊断定位enq:TX-rowlockcontention等待事件
在官方文档ID62354.1里面,提供了一个根据AWR快照ID查找那些段出现rowlock等待较多的SQL,这个也有一定的参考意义。

SELECTP.snap_id,
P.begin_interval_time,
O.owner,
O.object_name,
O.subobject_name,
O.object_type,
S.row_lock_waits_delta
FROMdba_hist_seg_statS,
dba_hist_seg_stat_objO,
dba_hist_snapshotP
WHERES.dbid=O.dbid
ANDS.ts#=O.ts#
ANDS.obj#=O.obj#
ANDS.dataobj#=O.dataobj#
ANDS.snap_id=P.snap_id
ANDS.dbid=P.dbid
ANDS.instance_number=P.instance_number
ANDS.row_lock_waits_delta>0
ANDP.snap_idBETWEEN<begin_snap>AND<end_snap>
ORDERBY1,3,4;

一般最常用的还是AWR报告结合ASH报告来诊断、定位enq:TX-rowlockcontention等待事件,另外,在TX-rowlockcontention的一些场景这篇分享文章中,对如何减少enq:TX-rowlockcontention等待做了一些总结,具体如下:
在一些事务频繁,并发较高的环境下,为了尽可能减少TX-rowlockcontention等待事件的发生,应当从应用设计到数据库多个层面进行考虑。
应用层面:
1、约束通常是为了保证数据完整性,在并发场景下,应充分考虑事务进行的逻辑顺序,避免多个会话事务交叉进行,触发约束冲突在事务级别发生竞争;
2、要提高并发效率,应当尽可能拆分大事务为小事务,提高txenqueue锁的获取释放速度;
3、如果要使用悲观锁(forupdate),应尽可能减少锁定的行范围;
数据库层面:
1、在dml频繁的表上建立适当的索引,提高SQL执行的效率,减少txenqueue锁持有的时间;避免全表扫描这种,容易造成IO开销巨大,热块竞争,会话堆积的访问方式。
2、在dml频繁的表上不应使用位图索引;
3、对于dml频繁的表,不应使用IOT表,物化视图等;
4、RAC环境下,对于批量的dml操作,尽可能固定在单一节点,尽量降低网络开销、集群竞争、一致性块获取和日志刷盘等带来的影响。

参考资料:

https://support.oracle.com/epmos/faces/DocumentDisplay?_afrLoop=521417992978367&id=62354.1&displayIndex=1&_afrWindowMode=0&_adf.ctrl-state=1d01vq0378_227
http://mp.weixin.qq.com/s?__biz=MjM5MzExMTU2OQ==&mid=2650603515&idx=1&sn=275956ad38d26168e44027336644e5a0&scene=2&srcid=0711qxhIykeqO278x7VZFx5k&from=timeline&isappinstalled=0#wechat_redirect
http://www.dbform.com/html/2015/2317.html
http://www.killdb.com/2015/07/13/%E5%85%B3%E4%BA%8Eenq-tx-row-lock-contention%E7%9A%84%E6%B5%8B%E8%AF%95%E5%92%8C%E6%A1%88%E4%BE%8B%E5%88%86%E6%9E%90.html
http://blog.chinaunix.net/uid-23284114-id-3390180.html
http://yunlongzheng.blog.51cto.com/788996/411205
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: