1 场景介绍
一个生产系统的数据库,Oracle单机迁移到Oracle RAC数据库,很快数据库里就出现了大量的行锁事件,连接数据库的十几台weblogic应用服务器报了大量的连接超时错误,应用也丢失了大量工单信息。
原因是什么呢?
获取了迁移后数据库的AWR报告,和迁移之前单机部署时的AWR报告对比一下,就发现迁移后数据库的行锁不但数量多了,等待的时间也增加了十几倍,最长的行锁等待时间达到30分钟以上。生产环境上的环境的数据不方便发到网上,本文通过常见的HR场景模拟并分析这种情况。
2 从AWR报告分析
数据库性能问题首先要分析的是AWR报告,报告的获取办法这里就不写了。分析行锁性能之前先看一下数据库的整体性能

DB time是报告采样时间的接近2倍,再看这台数据库服务器的配置1cpu,1核心,可以看到数据库可能会有性能问题。
下面看一下数据库的负载情况

数据库的DB time值很大, 超过了服务器的cpu数,DB CPU值接近0,DB time主要应该消耗在等待时间上,在从每事务执行的sql来看,数据库里的事务应该比较复杂,每个事务执行的语句达到69.3条,结合每个事务的逻辑读,redo size和block change来看,这应该是一个复杂事务型的数据库系统。
根据上面的分析,数据库可能有等待事件方面的问题,下面就需要查看前台等待事件,接着检查AWR报告中Top 10 Foreground Events by Total Wait Time。

行锁等待事件enq: TX - contention位于第一行,采样事件内,等待了1208次,总等待时间3589秒,这个事件的平均等待时间是2971.04ms,事件的总等待事件占DB time 96.1%,是DB time的主要消耗者。这种情况影响的不是数据库的整体性能,而是相关应用的性能。
前台等待事件更详细的信息位于Wait Events Statistics之下,这一部分可以看到等待事件的总体性信息,比如发生次数,平均等待时长,占用的数据库时间比例等,内容如下

行锁等待事件enq: TX - contention位于第一行,是总体等待时间处于第二位的等待事件,总体等待时间才1秒,占用的数据库时间比例5.57%,从这两点看来,这个事件对数据库的性能影响不大。事件的平均等待时间是21.10毫秒,每个事务发生1.09个等待事件,也就是说,这个等待事件的影响是普遍的,差不多每个事务都要等待这个等待事件,这个影响的范围是相当的广了,不能忽视。
分析到这里,对这个事件的影响已经有一个总体性的,大概的把握了。但是,对解决问题来说还不够,我们还需要知道是哪些应用产生了这个行锁,这些应用操作了哪些数据,做了什么样的操作?
产生行锁的表可以在数据库的AWR报告里查到,位于Segment Statistics的Segments by Row Lock Waits部分

这部分的内容如下

对test用户的两个表是产生行锁的根源,它们产生几乎一样多的行锁。要知道是哪个应用,哪条SQL语句产生的行锁,就需要查询数据库的ash报告了。
3 从ash报告分析
3.1 事件及其参数
ASH报告里可以看到采用时间段的top事件和top事件的参数值,top user事件如下

事件的参数值如下

通过行锁事件的第2个和第三个参数可以推算出是哪个事务导致了这个事件。
3.2 事件的相关SQL
在top SQL部分可以看到事件相关的SQL

点击SQL_ID可以看到完整的sql语句。如果SQL语句没有采用访问变量,通过sql语句就可以得出语句操作的是哪个表的哪部分数据,可以反馈给开发分析了。
3.3 事件的相关会话

这里显示的事件的会话信息,通过里面的program信息可以大致看出导致行锁的应用是哪一类的程序。这里面的会话id里面有实例信息,就是在括弧里面的数字。这个信息在Oracle rac环境里是十分有用的,如果事件的会话分布到不同的实例上,基本上可以认为是RAC环境恶化了等待事件的影响。
3.5 事件发生的时间

这部分信息也很有用,从这里可以看出哪个时间段发生的最频繁,和业务返回的问题时间段是不是重合,表里slot列是之间间隔内的采样次数,event count是这个时间段内这个时间发生的次数,以第一行为例,这个时间段内共采用了1262次,时间发生了1228次,几乎每个采样都发生了这个事件,行锁发生是十分频繁的。
4 用数据库脚本分析
上面通过awr和ash报告分析,我们得到了产生行锁的sql语句,行锁发生的最频繁的事件段,如果语句执行时也会知道是操作哪些数据时发生的行锁,如果时rac环境还会知道是阻塞会话,非阻塞会话在各个实例上分布。这些信息虽然很多,但是要采取措施还似乎缺少一些必要的信息。比如下面几个方面:
- 如果语句执行时使用了访问变量,我们又如何得知发生行锁时访问的数据。
- 如果是rac环境,我们怎么知道阻塞会话及被其阻塞的会话是在同一个实例上还是在不同的实例上。
- 如果行锁影响了应用的响应时间,我们需要知道行锁最长的等待时间是多少,会导致应用连接数据库超时吗?
上面这些信息,awr报告和ash报告都没有提供,需要在数据库内使用SQL来查询,近期的历史会话信息在V$ACTIVE_SESSION_HISTORY视图里查询,时间过去较长了,就需要在DBA_HIST_ACTIVE_SESS_HISTORY视图里查询了。
4.1 查询和分析时间的等待时间
SQL> l
1 select EVENT,max(WAIT_TIME),max(TIME_WAITED) from DBA_HIST_ACTIVE_SESS_HISTORY where
2 SAMPLE_TIME between to_date('2025-12-17 11:56:00', 'YYYY-MM-DD hh24:mi:ss')
3 and to_date('2025-12-17 12:02:00','YYYY-MM-DD hh24:mi:ss')
4* group by event
SQL> /
EVENT MAX(WAIT_TIME) MAX(TIME_WAITED)
---------------------------------------------------------------- --------------- ----------------
enq: TX - row lock contention 0 21509678
buffer busy waits 0 218143
17 0
WAIT_TIME是指会话上一次的等待时间的等待时间,如果采用时会话正在等待这个值是0,TIME_WAITED是会话已在这个等待事件上等待的时间。这两个时间的单位都是微妙。这两个值都有意义,哪个值过大都是事件的等待时间过长。从上面语句的输出可以看到,行锁等待事件最长时间长达21秒多,对数据库来说,这个时间已经很长了,通常会对应用的性能造成很大的影响,大概率会导致数据库连接超时。
下面这条sql语句以秒为单位,统计时间发生次数在时间上的分布
SQL> select EVENT, trunc((WAIT_TIME+TIME_WAITED)/1000000) WAIT_TIME_s ,count(*),count(distinct SESSION_ID) from DBA_HIST_ACTIVE_SESS_HISTORY
where SAMPLE_TIME between to_date('2025-12-17 11:56:00', 'YYYY-MM-DD hh24:mi:ss') and to_date('2025-12-17 12:02:00','YYYY-MM-DD hh24:mi:ss')
group by EVENT, trunc((WAIT_TIME+TIME_WAITED)/1000000); 2 3
EVENT WAIT_TIME_S COUNT(*) COUNT(DISTINCTSESSION_ID)
---------------------------------------------------------------- ----------- ---------- -------------------------
enq: TX - row lock contention 0 584 66
enq: TX - row lock contention 17 2 1
enq: TX - row lock contention 3 74 35
enq: TX - row lock contention 21 52 26
enq: TX - row lock contention 2 10 5
buffer busy waits 0 10 5
enq: TX - row lock contention 4 12 6
enq: TX - row lock contention 1 16 8
从上面输出可以看到,21秒以上等待时间采样到52次,有26个不同的会话,这个等待事件的影响范围也很很大。有了上面的事件信息,我们可以查询一下这些等待时间较长的会话及其阻塞会话在各个实例间的分布
4.2 行锁事件相关会话在实例间的分布分析
SQL> select EVENT, INSTANCE_NUMBER,SESSION_ID,BLOCKING_INST_ID,BLOCKING_SESSION,TIME_WAITED from DBA_HIST_ACTIVE_SESS_HISTORY
where SAMPLE_TIME between to_date('2025-12-17 11:56:00', 'YYYY-MM-DD hh24:mi:ss') and to_date('2025-12-17 12:02:00','YYYY-MM-DD hh24:mi:ss')
and TIME_WAITED>=20000000; 2 3
EVENT INSTANCE_NUMBER SESSION_ID BLOCKING_INST_ID BLOCKING_SESSION TIME_WAITED
---------------------------------------------------------------- --------------- ---------- ---------------- ---------------- -----------
enq: TX - row lock contention 1 140 1 120 21508641
enq: TX - row lock contention 1 144 1 120 21508771
enq: TX - row lock contention 1 146 1 120 21509384
enq: TX - row lock contention 1 150 1 120 21013971
enq: TX - row lock contention 1 151 1 120 21508560
enq: TX - row lock contention 1 153 1 120 21508475
enq: TX - row lock contention 1 154 1 120 21508419
enq: TX - row lock contention 1 81 1 120 21499062
enq: TX - row lock contention 1 82 1 120 21487559
enq: TX - row lock contention 1 85 1 120 21486437
enq: TX - row lock contention 1 86 1 120 21505717
EVENT INSTANCE_NUMBER SESSION_ID BLOCKING_INST_ID BLOCKING_SESSION TIME_WAITED
---------------------------------------------------------------- --------------- ---------- ---------------- ---------------- -----------
enq: TX - row lock contention 1 99 1 120 21489908
enq: TX - row lock contention 1 101 1 120 21492926
enq: TX - row lock contention 1 103 1 120 21502255
enq: TX - row lock contention 1 105 1 120 21509236
enq: TX - row lock contention 1 107 1 120 21507457
enq: TX - row lock contention 1 108 1 120 21491744
enq: TX - row lock contention 1 112 1 120 21488139
enq: TX - row lock contention 1 118 1 120 21490533
enq: TX - row lock contention 1 119 1 120 21508476
enq: TX - row lock contention 1 122 1 120 21495585
enq: TX - row lock contention 1 126 1 120 21509678
EVENT INSTANCE_NUMBER SESSION_ID BLOCKING_INST_ID BLOCKING_SESSION TIME_WAITED
---------------------------------------------------------------- --------------- ---------- ---------------- ---------------- -----------
enq: TX - row lock contention 1 131 1 120 21509507
enq: TX - row lock contention 1 132 1 120 21508960
enq: TX - row lock contention 1 138 1 120 21509091
enq: TX - row lock contention 1 139 1 120 21509608
enq: TX - row lock contention 1 81 1 120 21499062
......--省略多行
enq: TX - row lock contention 1 151 1 120 21508560
enq: TX - row lock contention 1 153 1 120 21508475
enq: TX - row lock contention 1 154 1 120 21508419
52 rows selected.
这里是单机环境,我遇到的是生产上RAC环境,INSTANCE_NUMBER 和BLOCKING_INST_ID 的值不同。这里可以看出,阻塞会话是同一个,应该是同一个应用导致的阻塞,其执行的SQL也可能是同一个,或者是数据维护时调整应用正在访问的数据所致。下面我们进一步查询一下是执行是执行哪个SQL导致的这个行锁。
4.3 行锁相关的SQL语句
SQL> select distinct EVENT,dbms_lob.SUBSTR(SQL_TEXT,200) sql_text from DBA_HIST_ACTIVE_SESS_HISTORY s left join DBA_HIST_SQLTEXT t on t.SQL_ID=s.SQL_ID
where s.SAMPLE_TIME between to_date('2025-12-17 11:56:00', 'YYYY-MM-DD hh24:mi:ss') and to_date('2025-12-17 12:02:00','YYYY-MM-DD hh24:mi:ss')
and s.TIME_WAITED>=20000000; 2 3
EVENT
----------------------------------------------------------------
SQL_TEXT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
enq: TX - row lock contention
UPDATE EMPLOYEES SET DEPT_ID = (SELECT DEPT_ID FROM DEPARTMENTS WHERE DEPT_NAME = :B2 ) WHERE DEPT_ID = (SELECT DEPT_ID FROM DEPARTMENTS WHERE DEPT_NAME = :B3 ) AND ROWNUM <= :B1
enq: TX - row lock contention
UPDATE EMPLOYEES SET SALARY = SALARY * (1 + :B2 ), STATUS = CASE WHEN SALARY * (1 + :B2 ) > 15000 THEN '高薪' ELSE STATUS END WHERE EMP_ID = :B1
这个事件等待事件超过20秒的会话执行了两条SQL语句。
在看一下阻塞会话的,从前一节的输出来看,阻塞会话的id是120,使用下面的语句来查询
SQL> select distinct EVENT,dbms_lob.SUBSTR(SQL_TEXT,200) sql_text from DBA_HIST_ACTIVE_SESS_HISTORY s left join DBA_HIST_SQLTEXT t on t.SQL_ID=s.SQL_ID
where s.SAMPLE_TIME between to_date('2025-12-17 11:56:00', 'YYYY-MM-DD hh24:mi:ss') and to_date('2025-12-17 12:02:00','YYYY-MM-DD hh24:mi:ss')
and s.SESSION_ID=120; 2 3
EVENT
----------------------------------------------------------------
SQL_TEXT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
enq: TX - row lock contention
UPDATE DEPARTMENTS SET BUDGET = BUDGET * 1.2 WHERE DEPT_ID = :B1
阻塞会话执行的语句只有一条。
到这里,等待事件最长的等待事件的起因已经完全清楚了,可以返回给应用调整程序了。
扩大一下范围,分析一下这个时间段内所有的会话及其阻塞会话执行的SQL语句,使用下面的SQL
SQL> ;
1 select distinct a.EVENT,dbms_lob.SUBSTR(ta.SQL_TEXT,200) sql_text,dbms_lob.SUBSTR(tb.SQL_TEXT,200) b_sql_text from DBA_HIST_ACTIVE_SESS_HISTORY a inner join DBA_HIST_ACTIVE_SESS_HISTORY b on a.BLOCKING_SESSION=b.SESSION_ID and a.SAMPLE_TIME=b.SAMPLE_TIME
2 left join DBA_HIST_SQLTEXT ta on ta.SQL_ID=a.SQL_ID
3 left join DBA_HIST_SQLTEXT tb on tb.SQL_ID=b.SQL_ID
4* where a.SAMPLE_TIME between to_date('2025-12-17 11:56:00', 'YYYY-MM-DD hh24:mi:ss') and to_date('2025-12-17 12:02:00','YYYY-MM-DD hh24:mi:ss')
EVENT
----------------------------------------------------------------
SQL_TEXT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
B_SQL_TEXT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
enq: TX - row lock contention
UPDATE EMPLOYEES SET DEPT_ID = (SELECT DEPT_ID FROM DEPARTMENTS WHERE DEPT_NAME = :B2 ) WHERE DEPT_ID = (SELECT DEPT_ID FROM DEPARTMENTS WHERE DEPT_NAME = :B3 ) AND ROWNUM <= :B1
UPDATE DEPARTMENTS SET BUDGET = BUDGET * 0.9, MANAGER_ID = NULL WHERE DEPT_ID = :B1
enq: TX - row lock contention
SELECT E.EMP_ID, E.EMP_NAME, E.SALARY, D.DEPT_NAME FROM EMPLOYEES E, DEPARTMENTS D WHERE E.DEPT_ID = D.DEPT_ID AND D.DEPT_NAME IN ('技术部', '销售部') AND ROWNUM <= 30 FOR UPDATE
UPDATE DEPARTMENTS SET BUDGET = BUDGET * 0.9, MANAGER_ID = NULL WHERE DEPT_ID = :B1
enq: TX - row lock contention
SELECT E.EMP_ID, E.EMP_NAME, E.SALARY, D.DEPT_NAME FROM EMPLOYEES E, DEPARTMENTS D WHERE E.DEPT_ID = D.DEPT_ID AND D.DEPT_NAME IN ('技术部', '销售部') AND ROWNUM <= 30 FOR UPDATE
UPDATE DEPARTMENTS SET BUDGET = BUDGET * 1.2 WHERE DEPT_ID = :B1
enq: TX - row lock contention
UPDATE DEPARTMENTS SET BUDGET = BUDGET * 1.2 WHERE DEPT_ID = :B1
UPDATE DEPARTMENTS SET BUDGET = BUDGET * 1.2 WHERE DEPT_ID = :B1
enq: TX - row lock contention
UPDATE EMPLOYEES SET SALARY = SALARY * (1 + :B2 ), STATUS = CASE WHEN SALARY * (1 + :B2 ) > 15000 THEN '高薪' ELSE STATUS END WHERE EMP_ID = :B1
UPDATE DEPARTMENTS SET BUDGET = BUDGET * 1.2 WHERE DEPT_ID = :B1
enq: TX - row lock contention
UPDATE EMPLOYEES SET SALARY = SALARY * (1 + :B2 ), STATUS = CASE WHEN SALARY * (1 + :B2 ) > 15000 THEN '高薪' ELSE STATUS END WHERE EMP_ID = :B1
UPDATE EMPLOYEES SET SALARY = SALARY * (1 + :B2 ), STATUS = CASE WHEN SALARY * (1 + :B2 ) > 15000 THEN '高薪' ELSE STATUS END WHERE EMP_ID = :B1
这个SQL阻塞链也可以发给开发全面解决一下行锁问题。
5 哪些数据,哪个用户导致行锁
在运维的过程中,也有些开发需要知道是访问哪些数据导致了行锁,这在多个业务系统共享一套数据时经常可以遇到,还是以上面的超过20秒的等待时间的行锁为例
SQL> select EVENT,to_char(SAMPLE_TIME,'YYYY-MM-DD hh24:mi:ss') SAMPLE_TIME , CURRENT_OBJ#,CURRENT_FILE#, CURRENT_BLOCK#,CURRENT_ROW# from DBA_HIST_ACTIVE_SESS_HISTORY
where SAMPLE_TIME between to_date('2025-12-17 11:56:00', 'YYYY-MM-DD hh24:mi:ss') and to_date('2025-12-17 12:02:00','YYYY-MM-DD hh24:mi:ss')
2 3 and TIME_WAITED>=20000000;
EVENT SAMPLE_TIME CURRENT_OBJ# CURRENT_FILE# CURRENT_BLOCK# CURRENT_ROW#
---------------------------------------------------------------- ------------------- ------------ ------------- -------------- ------------
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
EVENT SAMPLE_TIME CURRENT_OBJ# CURRENT_FILE# CURRENT_BLOCK# CURRENT_ROW#
---------------------------------------------------------------- ------------------- ------------ ------------- -------------- ------------
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
EVENT SAMPLE_TIME CURRENT_OBJ# CURRENT_FILE# CURRENT_BLOCK# CURRENT_ROW#
---------------------------------------------------------------- ------------------- ------------ ------------- -------------- ------------
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
EVENT SAMPLE_TIME CURRENT_OBJ# CURRENT_FILE# CURRENT_BLOCK# CURRENT_ROW#
---------------------------------------------------------------- ------------------- ------------ ------------- -------------- ------------
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
EVENT SAMPLE_TIME CURRENT_OBJ# CURRENT_FILE# CURRENT_BLOCK# CURRENT_ROW#
---------------------------------------------------------------- ------------------- ------------ ------------- -------------- ------------
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
enq: TX - row lock contention 2025-12-17 11:58:10 72505 1024 1443 78
52 rows selected.
可以看到这个行锁是在访问同一条数据时遇到的,怎样查询是哪条数据呢,可以dump这个数据块来看,这样比较麻烦,查起来也费劲。可以通过找到这行数据的rowid找到这条数据
--先找到这条数据的所在的表
SQL> select OWNER,OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID,OBJECT_TYPE from dba_objects where DATA_OBJECT_ID=72505;
OWNER OBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE
-------------------------------- -------------------------------- ---------- -------------- -----------------------
TEST EMPLOYEES 72505 72505 TABLE
--查询这条数据,条件是这条数据的rowid,及blockid
SQL> select EMP_ID,
rowid,
dbms_rowid.rowid_object(rowid) "object",
2 3 4 dbms_rowid.rowid_relative_fno(rowid) "file",
dbms_rowid.rowid_block_number(rowid) "block",
dbms_rowid.rowid_row_number(rowid) "row"
from TEST.EMPLOYEES where dbms_rowid.rowid_row_number(rowid)=78; 5 6 7
EMP_ID ROWID object file block row
---------- ------------------ ---------- ---------- ---------- ----------
79 AAARs5AAAAAAAWjABO 72505 0 1443 78
这里文件号对不上,查询一下数据库的数据文件信息,如下
SQL> l
1* select FILE_NAME,FILE_ID,RELATIVE_FNO from dba_data_files
SQL> /
FILE_NAME FILE_ID RELATIVE_FNO
---------------------------------------------------------------- ---------- ------------
/opt/oracle/oradata/FREE/FREEPDB1/sysaux01.dbf 13 1024
/opt/oracle/oradata/FREE/FREEPDB1/users01.dbf 15 1024
/opt/oracle/oradata/FREE/FREEPDB1/system01.dbf 12 1024
/opt/oracle/oradata/FREE/FREEPDB1/undotbs01.dbf 14 1024
这个是容器数据库,所有数据文件的RELATIVE_FNO都是1024,检查一下这几个表空间,都是bigfile表空间,RELATIVE_FNO这个值没什么意义了,上面查到的那条数据应是对的。
6 行锁的解决办法
分析的结果反馈到开发,生产环境是rac,等待行锁会话和它的阻塞会话大多不在一个实例上。在应用无法调整的情况下,调整了前端应用服务器的配置,将其数据库连接设置为主备配置,所有的应用服务器都连接到rac中的一个实例上。
调整后,行锁等待事件得到了很大的缓解,行锁的数量少了,行锁的等待时间也缩短了很多,应用服务器也不再发生连接超时的错误了。
7 行锁的原理及常见场景
当一条sql语句更新或删除一行数据时,事务只获得这一行的锁,获得的这个锁就是行锁。锁这一行的是某一个事务,而不是会话。oracle的规则是这样的,写一行数据库数据时加行锁,读一行数据不对行枷锁,写阻塞写,写不阻塞读,读不阻塞读和写。
当多个事务同时更新(包括删除) 统一数据时(表或索引),就会发生行锁竞争,在Oracle等待事件里就是enq: TX - row lock contention enq,enq时队列的意思,在oracle数据库里同锁是一个意思。
发生行锁竞争一个最常见的场合时多个会话同时更新或删除同一表的同一条数据,或者是会话更新一行数据的同时其它会话正在删除数据,反过来也一样。
表上有唯一索引时,插入数据有时也会导致行锁,这主要时由于插入数据时唯一索引的值引起的冲突,产生唯一索引的值的方法不正确,是表设计时唯一索引的选择不正确的结果。
产生行锁竞争的另一个常见的场景是位图索引,位图索引的一个值对应表中的多个值,虽然我们在表中更新的是不同的值,在位图索引中对应的是一个值,这样即使我们更新的表中的不同的值也会导致行锁竞争。这也是位图索引在oltp事务中被禁止使用的原因。
8 注意事项
等待事件的历史直方图统计信息在AWR报告里可以查到,我这个环境是Oracle 26 AI free版数据库,没有这部分信息。企业版的19C里面有这一部分如图

在Wait Events Statistics部分里有Wait Event Histogram,依据等待时间的不同分成了6个部分。下面是等待时间最长的那bufen

这部分是等待事件在时间上的分布的统计 信息,以第一行buffer busy waits为例,可以看到等待时间从4分钟到1小时的有11个。在后面的% of Total Waits部分里可以看到100%的等待事件在2分钟之内,这个值应该是近似值,误差在0.5%以内,这部分的注释说了,.0意味着小于0.5%,空值才是真正的0。这一部分的显示似乎有些矛盾,前面说的4分钟到1小时的有11个,后面的百分比在相应的等待事件段内都是空值,也就是真正的0。这和awr报告统计的方法有关,具体哪个更准确,这就需要我们去数据库里去查询会话历史信息进行检验核实。
这一部分对我们全面分析等待事件的影响是十分有帮助的。




