Locks caused by Oracle uncommitted transactions

The author's company recently installed a system that uses middleware to connect to the Oracle database. After a period of use, the system stops responding. Found that the problem lies in 2 points:

1. After a thread in the middleware executes the Delete statement, it has been in a waiting state. There is no COMMIT to submit the transaction. A row lock is imposed on the table and the thread cannot be reused (the total number of threads in the middleware is limited)

2. After applying a row lock to the table, subsequent middleware threads will be blocked if they need to modify the row.

The above two factors continue to occur, eventually leading to the exhaustion of the number of middleware threads, and the system stops responding.

The following will be divided into two parts. The first part is to directly give the SQL statement to judge the above faults. The second part is to reproduce the problem by doing experiments.

1. Directly judge the row lock of the table caused by the uncommitted transaction

1.1 Determine which SESSION executes DML (Insert/Update/Delete) but does not submit (Commit), causing row locks

--找到修改了数据,但是未提交的Session,选择WAIT_CALSS='Idle',也就是Session处于休息状态,但是有锁定的表
----找到修改了数据,但是未提交的Session,选择WAIT_CALSS='Idle',也就是Session处于休息状态,但是有锁定的表
 SELECT A.SID,
       A.SERIAL#,
       A.USERNAME,
       A.EVENT,
       A.WAIT_CLASS,
       A.SECONDS_IN_WAIT,
       A.PREV_EXEC_START,
       b.LOCKED_MODE,
       C.OWNER,
       C.OBJECT_NAME,
       C.OBJECT_TYPE
  FROM V$SESSION A
 INNER JOIN V$LOCKED_OBJECT B ON A.SID = b.SESSION_ID
 INNER JOIN DBA_OBJECTS C ON B.OBJECT_ID = c.OBJECT_ID
 WHERE A.WAIT_CLASS = 'Idle'
   AND A.SECONDS_IN_WAIT > 10--/*SESSION空闲后一段时间还锁定的才算有问题,这里随便给了个数值10秒*/

If you confirm that there is indeed a problem with these SESSIONs, you can kill them directly, and pass in the SID and SERIAL# of the previous statement

ALTER SYSTEM KILL SESSION 'SID,SERIAL#'

Note: ORACLE has a very interesting place, that is, you need to fill in ""SERIAL#" when KILL SESSION, in order to avoid getting a SID when you just queried, and then you plan to kill him. The lost session ends the disconnection, and then the new session reuses this SID, that is, to avoid manslaughter, SQL SERVER can directly KILL.

1.2 Determine which SESSION is blocked, and locate who is blocked, which table and which row caused the blocking

----当SESSION被阻塞,通过ROW_WAIT_OBJ#,ROW_WAIT_FILE#,ROW_WAIT_BLOCK#,ROW_WAIT_ROW#这几个字段找到ROWID,然后通过ROWID找到被锁定的记录
SELECT BLOCKING_SESSION,
       SID,
       SERIAL#,
       AUDSID,
       PADDR,
       USER#,
       USERNAME,
       EVENT,
       WAIT_CLASS,
       SECONDS_IN_WAIT,
       ROW_WAIT_OBJ#,
       ROW_WAIT_FILE#,
       ROW_WAIT_BLOCK#,
       ROW_WAIT_ROW#,
       BLOCKING_SESSION_STATUS,
       BLOCKING_INSTANCE,
       C.OWNER,
       C.OBJECT_NAME,
       C.OBJECT_TYPE,
       dbms_rowid.rowid_create(1,
                               ROW_WAIT_OBJ#,
                               ROW_WAIT_FILE#,
                               ROW_WAIT_BLOCK#,
                               ROW_WAIT_ROW#)
  FROM V$SESSION A
 INNER JOIN V$LOCKED_OBJECT B ON A.SID = b.SESSION_ID
 INNER JOIN DBA_OBJECTS C ON B.OBJECT_ID = c.OBJECT_ID
 WHERE BLOCKING_SESSION IS NOT NULL;

Through the OBJECT_NAME and ROWID returned above, it is known that the blocking is caused by that row.

SELECT * FROM 前面返回的表名称
where ROWID=前面返回的ROWID

example:

SQL> select * from test.TESTLOCK where rowid='AAASbJAAEAAAACvAAB';
        ID NAME
---------- --------------------------------------------------------------------------------
         2 kkkkkkkkkkkkkk

1.3 Which sessions are blocked, which are blocked and the executed Sql statement

SELECT s.BLOCKING_SESSION,
       l.session_id sid,
       s.serial#,
       l.locked_mode,
       l.oracle_username,
       s.user#,
       l.os_user_name,
       s.machine,
       s.terminal,
       a.sql_text,
       a.action
  FROM v$sqlarea a, v$session s, v$locked_object l
 WHERE l.session_id = s.sid
   AND s.prev_sql_addr = a.address
 ORDER BY sid, s.serial#;

1.4 Query which table is locked and who locked it

select s.sid,
           s.serial#,
           lo.oracle_username,
           lo.os_user_name,
           ao.object_name  as 被锁表名称table_locked_name,
           s.username,
           s.schemaname,
           s.osuser,
           s.process,
           s.machine,
           s.terminal,
           lo.locked_mode
      from v$locked_object lo, all_objects ao, v$session s
     where ao.object_id = lo.object_id
       and lo.session_id = s.sid
     order by s.sid asc;

2. Reproduce the whole process through the experimental process

First of all, the Oracle database is write-blocking, and the reading and writing are non-blocking. That is, if the following two statements are executed in different windows (SESSION), since they both update the same row, if the first one is executed Without COMMIT, the later execution will always be blocked:

--第一个窗口执行如下语句
UPDATE TESTLOCK
SET AAA=11
WHERE AAA=1

--第二个窗口执行以下语句
UPDATE TESTLOCK
SET AAA=12
WHERE AAA=1

Let's start our experiment, the Oracle version is 11G, with the following questions:

1) How do you know that a connection has modified data, but it has not been submitted, resulting in a lock on the table?
2) For question 1, can you know which record in which table is specifically locked?

First, we create three windows on the PS/SQL client, which are: test window 1, test window 2, and observation window. The experiment is logged in with the "TEST" account. In order to avoid other SESSIONs from interfering with our experiments, many statements add "WHERE USERNAME='TEST'" as a limitation, if you want to repeat the experiment process, you need to modify this part of the statement.

2.1 Create a test table in test window 1 and insert 10 records

/*初始化测试表*/

--建立一个测试表
create table TESTLOCK
(
  aaa number not null,
  bbb nvarchar2(10) not null,
  ccc nvarchar2(10) not null
);
create INDEX PK_TESTLOCK on TESTLOCK (aaa);

 
--随便插入点数据
INSERT INTO TESTLOCK VALUES ('1','BBB','CCC');
INSERT INTO TESTLOCK VALUES ('2','BBB','CCC');
INSERT INTO TESTLOCK VALUES ('3','BBB','CCC');
INSERT INTO TESTLOCK VALUES ('4','BBB','CCC');
INSERT INTO TESTLOCK VALUES ('5','BBB','CCC');
INSERT INTO TESTLOCK VALUES ('6','BBB','CCC');
INSERT INTO TESTLOCK VALUES ('7','BBB','CCC');
INSERT INTO TESTLOCK VALUES ('8','BBB','CCC');
INSERT INTO TESTLOCK VALUES ('9','BBB','CCC');
INSERT INTO TESTLOCK VALUES ('10','BBB','CCC');

 
--然后我们的表里面就有了一些数据
SELECT * FROM TESTLOCK;
COMMIT;

2.2 In the test window 2, query the test table, you can see the 10 records added in step 1

SELECT * FROM TESTLOCK;

insert image description here

2.3 Observe the current Session and table locks in the observation window

Here we use three Oracle system attempts:
V$SESSION , V$LOCK , V$LOCKED_OBJECT

--当前的Session情况
SELECT SID,SERIAL#,AUDSID,PADDR,USER#,USERNAME,EVENT,WAIT_CLASS,SECONDS_IN_WAIT,ROW_WAIT_OBJ#,ROW_WAIT_FILE#,ROW_WAIT_BLOCK#,ROW_WAIT_ROW#,      BLOCKING_SESSION_STATUS,BLOCKING_INSTANCE,BLOCKING_SESSION
FROM V$SESSION
WHERE USERNAME='TEST';

--当前TESTLOCK表锁的情况
SELECT * FROM V$LOCK
WHERE TYPE='TM'
AND ID1=(SELECT OBJECT_ID FROM DBA_OBJECTS
                 WHERE OBJECT_NAME='TESTLOCK');

SELECT XIDUSN,XIDSLOT,XIDSQN,OBJECT_ID,SESSION_ID,ORACLE_USERNAME,PROCESS,LOCKED_MODE
FROM V$LOCKED_OBJECT
WHERE OBJECT_ID=(SELECT OBJECT_ID FROM DBA_OBJECTS
                 WHERE OBJECT_NAME='TESTLOCK');

It can be seen from the figure below that the TEST account has generated a total of 4 sessions, which are PLSQL itself connected to the database and the three windows we created:
insert image description here
we focus on several fields:

EVENT: the data or event that the oracle session is waiting for

WAIT_CLASS: The name of the wait event

blocking_session_status: If the blocking_session_status field is VALID, it means that the SESSION is blocked

blocking_session: Which Session is blocked by

Through the above four observations, no Session is blocked, the current three Sessions are waiting for client messages (EVENT=SQL Net message from client, WAIT_CLASS=Idle), and the remaining Session is sending messages to the client (EVENT=SQL Net message from client, Net message to client, WAIT_CLASS=Network), is our current observation window

The next two statements querying V$LOCK and V$LOCKED_OBJECT do not return anything, indicating that the current TESTLOCK table is not locked

insert image description here
insert image description here

2.4 Update table TESTLOCK in test window 1, but do not submit (Commit)

UPDATE TESTLOCK
SET AAA=11
WHERE AAA=1

The commit and rollback icons will be displayed in the upper left corner of PL/SQL, indicating that the transaction has not been committed
insert image description here

2.5 Execute the statement in step 3 again in the observation window:

Through the query of V$LOCK and V$LOCKED_OBJECT, we can know that SID=1947 locks the TESTLOCK table, where LMODE=3 (row-level exclusive lock, which is generated by UPDATE here)
insert image description here
insert image description here
insert image description here
insert image description here

2.6 Back to the first question before, how to know that a SESSION has modified data but has not COMMIT, and execute the following statement in the observation window:

--找到修改了数据,但是未提交的Session,选择WAIT_CALSS='Idle',也就是Session处于休息状态,但是有锁定的表
SELECT A.SID,A.SERIAL#,A.USERNAME,A.EVENT,A.WAIT_CLASS,A.SECONDS_IN_WAIT,A.PREV_EXEC_START,b.LOCKED_MODE,C.OWNER,C.OBJECT_NAME,C.OBJECT_TYPE
FROM V$SESSION A
INNER JOIN V$LOCKED_OBJECT B
ON A.SID=b.SESSION_ID
INNER JOIN DBA_OBJECTS C
ON B.OBJECT_ID=c.OBJECT_ID
WHERE A.WAIT_CLASS='Idle'
AND A.SECONDS_IN_WAIT>10/*SESSION空闲后一段时间还锁定的才算有问题,这里随便给了个数值10秒*/
AND USERNAME='TEST';

You only need to judge that WAIT_CLASS='Idle', and at the same time there is a locked object in V$LOCKED_OBJECT and the SESSION has been idle for a period of time, as shown in the figure, you can judge that SID=1947 has locked the TESTLOCK table, SECONDS_IN_WAIT can be considered as the length of the lock, and the unit is Second
insert image description here

2.7 Execute the following statements in the observation window to observe from the perspective of transactions

--从事务角度观察,连接v$session和v$transaction
SELECT A.SID,A.SERIAL#,A.USERNAME,A.EVENT,A.WAIT_CLASS,A.SECONDS_IN_WAIT,A.PREV_EXEC_START,b.START_DATE
FROM v$session a
INNER JOIN v$transaction b
ON a.taddr=b.addr
WHERE USERNAME='TEST'

After connecting the two views, you can know that SID=1947 started the transaction
insert image description here

2.8 Next, let's verify that the record is updated and deleted in test window 2, but the updated and deleted record is not the same record as that in test window 1. The record updated in test window 1 is AAA=1, and it is submitted immediately after the update and deletion ( COMMIT)

--更新和测试窗口1不同的记录
UPDATE TESTLOCK
SET AAA=100
WHERE AAA=2;
COMMIT ;

--删除和测试窗口1不同的记录
DELETE FROM TESTLOCK
WHERE AAA=3;
COMMIT;

SELECT * FROM TESTLOCK;

It can be seen that the update and deletion are not blocked. The result of the query table is shown in the figure. It can be seen that the data of AAA=2 is updated, the data of AAA=3 is deleted, and the data of AAA=1 is still the same as before, that is, it cannot be seen The data update to test window 1, that is, any data that has not been submitted cannot be seen.
insert image description here

2.9 Let's verify: what happens if another connection modifies the same record of TESTLOCK when the previous UPDATE is not submitted?

Execute the following statement in test window 2: but do not commit (COMMIT)

--更新和测窗口1相同的记录
UPDATE TESTLOCK
SET AAA=12
WHERE AAA=1

The statement will always be in the "executing" state. In fact, there is a row lock on TESTLOCK, and the SESSION has been waiting for the previous row lock to be released.
insert image description here

2.10 Execute the statement in step 3 in the observation window again:

Observe SID=9, display EVENT='enq: TX - row lock contention', which means waiting for a row lock to be released, BLOCKING_SESSION indicates that the SESSION is blocked by SID=1947, that is, the SESSION of test window 1 observes V$LOCK to
insert image description here
distinguish No matter which LOCK is not submitted, which is blocked, the display of the two LOCKs is the same

insert image description here
Observe V$LOCKED_OBJECT, which can be judged by XINUSN/XIDSLOT/XIDSQN. These three fields are related to rollback. If they are all 0, it can be judged as blocked
insert image description here

2.11 Find the specific row that is locked

Execute the following statement in the watch window:

--当SESSION被阻塞,通过ROW_WAIT_OBJ#,ROW_WAIT_FILE#,ROW_WAIT_BLOCK#,ROW_WAIT_ROW#这几个字段找到ROWID,然后通过ROWID找到被锁定的记录
SELECT SID,SERIAL#,AUDSID,PADDR,USER#,USERNAME,EVENT,WAIT_CLASS,SECONDS_IN_WAIT,ROW_WAIT_OBJ#,ROW_WAIT_FILE#,ROW_WAIT_BLOCK#,ROW_WAIT_ROW#,
        BLOCKING_SESSION_STATUS,BLOCKING_INSTANCE,BLOCKING_SESSION,C.OWNER,C.OBJECT_NAME,C.OBJECT_TYPE
        ,dbms_rowid.rowid_create(1,ROW_WAIT_OBJ#,ROW_WAIT_FILE#,ROW_WAIT_BLOCK#,ROW_WAIT_ROW#)
FROM V$SESSION A
INNER JOIN V$LOCKED_OBJECT B
ON A.SID=b.SESSION_ID
INNER JOIN DBA_OBJECTS C
ON B.OBJECT_ID=c.OBJECT_ID
WHERE USERNAME='TEST'
AND BLOCKING_SESSION IS NOT NULL ;

To obtain the ROWID of the specific blocked table, the statement is dbms_rowid.rowid_create(1,ROW_WAIT_OBJ#,ROW_WAIT_FILE#,ROW_WAIT_BLOCK#,ROW_WAIT_ROW#)
insert image description here
query to obtain the specific blocked record, which happens to be the record we updated in the test window 1

--通过前面的函数rowid_create获得具体的ROWID,然后在锁定表中查询记录
SELECT * FROM TESTLOCK
where ROWID='AAJ2QDAAnAAGrwnAAA'

insert image description here

2.12 Kill the SESSION that blocks other connections

It can be seen from the front that SESSION 1947 blocks the current thread, query SID=1947, query its SERIAL#, and then execute SQL to kill the SESSION
insert image description here

ALTER SYSTEM KILL SESSION '1947,63353'

2.13 Observe the current SESSION again, SID=9, no longer blocked

SELECT SID,SERIAL#,AUDSID,PADDR,USER#,USERNAME,EVENT,WAIT_CLASS,SECONDS_IN_WAIT,ROW_WAIT_OBJ#,ROW_WAIT_FILE#,ROW_WAIT_BLOCK#,ROW_WAIT_ROW#,
        BLOCKING_SESSION_STATUS,BLOCKING_INSTANCE,BLOCKING_SESSION
FROM V$SESSION
WHERE USERNAME='TEST';

insert image description here
The field explanation of V$SESSION can be found at:
https://docs.oracle.com/cd/E18283_01/server.112/e17110/dynviews_3016.htm

Guess you like

Origin blog.csdn.net/Ruishine/article/details/129184021