구분 | 설명 |
---|---|
DML 락 | DML 락은 INSERT, UPDATE, MERGE 그리고 DELETE 문을 말함. 또한 이는 데이터를 동시에 변경할 수 있는 메커니즘을 제공함. |
DDL 락 | DDL 락은 CREATE, ALTER 문 등을 의미함. DDL 락은 객체 구조의 정의를 보호함. |
내부 락과 래치 | 오라클은 내부 자료구조를 보호하기 위해 내부 락(internal lock)과 래치(latch)를 사용함. 예를들어, 오라클은 쿼리를 파싱하고 최적화된 실행계획을 생성하고, 라이브러리 캐시를 래칭한 다음, 다른 세션이 공유할 수 있도록 실행계획을 라이브러리 캐시에 저장함. 래치는 오라클이 사용하는 경량의 저수준 직렬(serialization) 장치이며, 기능에서는 락과 비슷함. |
1 | 락을 걸고 싶은 로우의 주소를 찾는다. |
2 | 락 관리자에 줄을 선다(락 관리자는 직렬화 되어야 함) |
3 | 리스트에 락을 건다. |
4 | 리스트를 검색하여 다른 누군가 해당 로우에 락을 걸었는지 조회한다. |
5 | 여러분이 락을 걸었다는 사실을 입증하기 위해 리스트에 새 엔트리를 만든다. |
6 | 리스트에 락을 푼다. |
1 | 다시 락 관리자에 줄을 선다. |
2 | 락 리스트에 락을 건다. |
3 | 리스트를 검색하여 설정한 락을 해제한다. |
4 | 리스트에 락을 푼다. |
1 | 락을 걸고 싶은 로우의 주소를 찾는다. |
2 | 해당 로우로 간다. |
3 | 그 자리에 있는 로우에 락을 건다. 어딘가에 따로 존재하는 긴 리스트에 락을 거는 것이 아니라, 로우가 있는 위치에 락은 건다( (NOWAIT 옵션을 사용하지 않았고, 로우에 이미 락이 걸렸다면 락을 건 트랜잭션이 끝날 때까지 기다린다) |
구분 | 설명 |
---|---|
V$TRANSACTION | 활동 중인 모든 트랜잭션에 대한 엔트리를 담고 있다. |
V$SESSION | 로그인을 한 세션을 보여준다. |
V$LOCK | 락을 기다리는 세션뿐만 아니라 보유 중인 모든 enqueue 락에 대한 엔트리를 포함하고 있다. 이 뷰에서는 세션 락이 건 각 로우에 대한 정보는 볼 수 없으며, 로우가 한개든 수백맨 로우든 이 뷰는 단지 한 개의 로우만 보여준다. |
-- 1. (Session_1) 샘플 데이터 생성
DROP TABLE EMP;
DROP TABLE DEPT;
CREATE TABLE EMP
(EMPNO NUMBER(4) NOT NULL,
ENAME VARCHAR2(10),
JOB VARCHAR2(9),
MGR NUMBER(4),
HIREDATE DATE,
SAL NUMBER(7, 2),
COMM NUMBER(7, 2),
DEPTNO NUMBER(2));
INSERT INTO EMP VALUES
(7369, 'SMITH', 'CLERK', 7902,
TO_DATE('17-DEC-1980', 'DD-MON-YYYY'), 800, NULL, 20);
INSERT INTO EMP VALUES
(7499, 'ALLEN', 'SALESMAN', 7698,
TO_DATE('20-FEB-1981', 'DD-MON-YYYY'), 1600, 300, 30);
INSERT INTO EMP VALUES
(7521, 'WARD', 'SALESMAN', 7698,
TO_DATE('22-FEB-1981', 'DD-MON-YYYY'), 1250, 500, 30);
INSERT INTO EMP VALUES
(7566, 'JONES', 'MANAGER', 7839,
TO_DATE('2-APR-1981', 'DD-MON-YYYY'), 2975, NULL, 20);
INSERT INTO EMP VALUES
(7654, 'MARTIN', 'SALESMAN', 7698,
TO_DATE('28-SEP-1981', 'DD-MON-YYYY'), 1250, 1400, 30);
INSERT INTO EMP VALUES
(7698, 'BLAKE', 'MANAGER', 7839,
TO_DATE('1-MAY-1981', 'DD-MON-YYYY'), 2850, NULL, 30);
INSERT INTO EMP VALUES
(7782, 'CLARK', 'MANAGER', 7839,
TO_DATE('9-JUN-1981', 'DD-MON-YYYY'), 2450, NULL, 10);
INSERT INTO EMP VALUES
(7788, 'SCOTT', 'ANALYST', 7566,
TO_DATE('09-DEC-1982', 'DD-MON-YYYY'), 3000, NULL, 20);
INSERT INTO EMP VALUES
(7839, 'KING', 'PRESIDENT', NULL,
TO_DATE('17-NOV-1981', 'DD-MON-YYYY'), 5000, NULL, 10);
INSERT INTO EMP VALUES
(7844, 'TURNER', 'SALESMAN', 7698,
TO_DATE('8-SEP-1981', 'DD-MON-YYYY'), 1500, 0, 30);
INSERT INTO EMP VALUES
(7876, 'ADAMS', 'CLERK', 7788,
TO_DATE('12-JAN-1983', 'DD-MON-YYYY'), 1100, NULL, 20);
INSERT INTO EMP VALUES
(7900, 'JAMES', 'CLERK', 7698,
TO_DATE('3-DEC-1981', 'DD-MON-YYYY'), 950, NULL, 30);
INSERT INTO EMP VALUES
(7902, 'FORD', 'ANALYST', 7566,
TO_DATE('3-DEC-1981', 'DD-MON-YYYY'), 3000, NULL, 20);
INSERT INTO EMP VALUES
(7934, 'MILLER', 'CLERK', 7782,
TO_DATE('23-JAN-1982', 'DD-MON-YYYY'), 1300, NULL, 10);
CREATE TABLE DEPT
(DEPTNO NUMBER(2),
DNAME VARCHAR2(14),
LOC VARCHAR2(13) );
INSERT INTO DEPT VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO DEPT VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO DEPT VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO DEPT VALUES (40, 'OPERATIONS', 'BOSTON');
COMMIT;
/
ALTER TABLE DEPT ADD CONSTRAINT DEPT_PK PRIMARY KEY(DEPTNO);
ALTER TABLE EMP ADD CONSTRAINT EMP_PK PRIMARY KEY(EMPNO);
ALTER TABLE EMP ADD CONSTRAINT EMP_FK_DEPT FOREIGN KEY(DEPTNO) REFERENCES DEPT(DEPTNO);
CREATE INDEX EMP_DEPTNO_IDX ON EMP(DEPTNO);
-- 2. (Session_1) 트랜잭션 시작
UPDATE DEPT
SET DNAME = INITCAP(DNAME)
;
4 rows updated.
;
-- 3. (Session_1) Lock 모니터링
/***************************************************************************************************************************/
-- LMODE가 6이라는 것은 배타 락을 의미하며, REQUEST가 0 이라는 것은 락을 요청하지 않았지만 락을 갖고 있다는 것을 의미함.
-- V$LOCK에는 여러개의 로우가 락이 걸려 있더라도 한개의 로우만 보여줌.
-- L.ID1을 통해 V$TRANSACTION의 XIDUSN, XIDSLOT 2개 값을 얻을 수 있고, L.ID2와 V$TRANSACTION의 XIDSQN 와는 동일함.
-- 즉, V$LOCK에서 ID1, ID2를 통해 V$TRANSACTION의 트랜잭션 ID를 얻을 수 있음.
/***************************************************************************************************************************/
@mysess
;
MY_SESSION_INFO
--------------------------------------------------
DB Name : DB
Sid, Serial# : 9563, 45
OS Process : 3952:4172 (CPID), 684848 (SPID)
DB User : USER
OS User : raxsoft
Module Info : 01@ mysess.sql
Program Info : 01@ mysess.sql
Machine[Term] : RAXSOFT [RAXSOFT]
;
SELECT S.USERNAME,
L.SID,
L.ID1,
TRUNC(L.ID1 / POWER(2, 16)) RBS,
BITAND(L.ID1, TO_NUMBER('ffff', 'xxxx')) + 0 SLOT,
L.ID2 SEQ,
L.LMODE,
L.REQUEST
FROM V$LOCK L,
V$SESSION S
WHERE L.SID = S.SID
AND L.TYPE = 'TX'
AND S.USERNAME = USER
;
USERNAME SID ID1 RBS SLOT SEQ LMODE REQUEST
--------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
APPS 9563 1363148812 20800 12 3 6 0
;
SELECT XIDUSN,
XIDSLOT,
XIDSQN,
TO_CHAR(XIDUSN, 'XXXX') || TO_CHAR(XIDSLOT, 'XXXX') || TO_CHAR(XIDSQN, 'XXXX') TO_HEX_XID,
XID
FROM V$TRANSACTION
;
XIDUSN XIDSLOT XIDSQN TO_HEX_XID XID
---------- ---------- ---------- ------------------------------ ----------------
20800 12 3 5140 C 3 5140000C00000003
;
-- 4. (Session_2) EMP, DEPT 테이블 수정
@mysess
;
MY_SESSION_INFO
-----------------------------------------------------
DB Name : DB
Sid, Serial# : 9604, 45
OS Process : 3448:5492 (CPID), 656242 (SPID)
DB User : USER
OS User : raxsoft
Module Info : 01@ mysess.sql
Program Info : 01@ mysess.sql
Machine[Term] : RAXSOFT [RAXSOFT]
;
UPDATE EMP
SET ENAME = UPPER(ENAME)
;
14 rows updated.
UPDATE DEPT
SET DEPTNO = DEPTNO - 10
;
-- 5. (Session_3) Lock 모니터링
/***************************************************************************************************************************/
-- V$LOCK에서 보면 Session_1(SID : 9563)과 Session_2(SID : 9604) 2개 모두 REQUEST가 0인 락을 보유하고 있음(블로킹 세션)
-- 또한 Session_2(SID : 9604)는 REQUEST가 6인 상태이므로 락으로 인해 대기중인 상태.
-- 여기서 Session_2는 Session_1의 XID와 값이 같기 때문에 같은 트랜잭션을 처리하고 있는 것을 확인할 수 있음.
/***************************************************************************************************************************/
@mysess
;
MY_SESSION_INFO
-------------------------------------------------
DB Name : DB
Sid, Serial# : 9595, 67
OS Process : 2480:4776 (CPID), 652122 (SPID)
DB User : USER
OS User : raxsoft
Module Info : 01@ mysess.sql
Program Info : 01@ mysess.sql
Machine[Term] : RAXSOFT [RAXSOFT]
;
SELECT S.USERNAME,
L.SID,
L.ID1,
TRUNC(L.ID1 / POWER(2, 16)) RBS,
BITAND(L.ID1, TO_NUMBER('ffff', 'xxxx')) + 0 SLOT,
L.ID2 SEQ,
L.LMODE,
L.REQUEST
FROM V$LOCK L,
V$SESSION S
WHERE L.SID = S.SID
AND L.TYPE = 'TX'
AND S.USERNAME = USER
;
USERNAME SID ID1 RBS SLOT SEQ LMODE REQUEST
--------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
APPS 9604 1363148812 20800 12 3 0 6 -- 같은 트랜잭션
APPS 9563 1363148812 20800 12 3 6 0 -- 같은 트랜잭션
APPS 9604 1363869742 20811 46 2 6 0
;
SELECT XIDUSN,
XIDSLOT,
XIDSQN,
TO_CHAR(XIDUSN, 'XXXX') || TO_CHAR(XIDSLOT, 'XXXX') || TO_CHAR(XIDSQN, 'XXXX') TO_HEX_XID,
XID
FROM V$TRANSACTION
;
XIDUSN XIDSLOT XIDSQN TO_HEX_XID XID
---------- ---------- ---------- ------------------------------ ----------------
20800 12 3 5140 C 3 5140000C00000003
20811 46 2 514B 2E 2 514B002E00000002
;
-- 6. (Session_3) V$LOCK 셀프조인 모니터링
SELECT (SELECT USERNAME
FROM V$SESSION
WHERE SID = A.SID) BLOCKER,
A.SID,
' is blocking ',
(SELECT USERNAME
FROM V$SESSION
WHERE SID = B.SID) BLOCKEE,
B.SID
FROM V$LOCK A,
V$LOCK B
WHERE A.ID1 = B.ID1
AND A.ID2 = B.ID2
AND A.BLOCK = 1 -- Lock을 잡고 있는 세션
AND B.REQUEST > 0 -- Lock으로 인해 대기하는 세션
;
BLOCKER SID 'ISBLOCKING' BLOCKEE SID
-------- ---------- -------------------------- -------- ----------
APPS 9563 is blocking APPS 9604
;
-- 7. (Session_1) 트랜잭션 Commit한 이후 Lock 모니터링
COMMIT
;
SELECT S.USERNAME,
L.SID,
L.ID1,
TRUNC(L.ID1 / POWER(2, 16)) RBS,
BITAND(L.ID1, TO_NUMBER('ffff', 'xxxx')) + 0 SLOT,
L.ID2 SEQ,
L.LMODE,
L.REQUEST
FROM V$LOCK L,
V$SESSION S
WHERE L.SID = S.SID
AND L.TYPE = 'TX'
AND S.USERNAME = USER
;
USERNAME SID ID1 RBS SLOT SEQ LMODE REQUEST
---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
USER 9604 1363869742 20811 46 2 6 0
;
SELECT XIDUSN,
XIDSLOT,
XIDSQN,
TO_CHAR(XIDUSN, 'XXXX') || TO_CHAR(XIDSLOT, 'XXXX') || TO_CHAR(XIDSQN, 'XXXX') TO_HEX_XID,
XID
FROM V$TRANSACTION
;
XIDUSN XIDSLOT XIDSQN TO_HEX_XID XID
---------- ---------- ---------- ------------------------------ ----------------
20811 46 2 514B 2E 2 514B002E00000002
;
구분 | 설명 |
---|---|
INITRANS | 블록에 동시에 엑세스 가능한 트렉젝션의 초기수를 나타내며, 기본값은 2이다(딕셔너리에서는 1로 관찰되어도 실제 값은 2임) |
MAXTRANS | 동시에 엑세스 가능한 트렉젝션의 최대값으로 INITRANS의 상대적 개념이며, 기본값은 255이다. |
-- 1. 샘플 데이터 생성
DROP TABLE T;
CREATE TABLE T (X INT PRIMARY KEY,
Y VARCHAR2(4000))
;
INSERT INTO T
SELECT ROWNUM,
RPAD('*', 148, '*')
FROM DUAL
CONNECT BY LEVEL <= 46
;
-- 2. 데이터 블록 확인
/***************************************************************************************************************************/
-- 테이블 T의 동일한 블록에 46개의 로우를 담았고, 이를 데이터로 확인함.
-- (만약, 148개의 문자열이 아니라 조금 더 클 경우 2개의 블록에 나누어 저장해야 함.
/***************************************************************************************************************************/
SELECT LENGTH(Y),
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) BLK,
COUNT(*),
MIN(X),
MAX(X)
FROM T
GROUP BY LENGTH(Y),
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
;
LENGTH(Y) BLK COUNT(*) MIN(X) MAX(X)
---------- ---------- ---------- ---------- ----------
148 1377886 46 1 46
;
-- 3. DO_UPDATE 프로시저 생성
/***************************************************************************************************************************/
-- DO_UPDATE 프로시저는 재귀호출을 통해 LOCK을 걸 때 트랜잭션 슬롯을 몇개까지 사용할 수 있는지 확인하는 내용.
-- 하나의 블록에 데이터가 꽉 차있을 경우와, 빈 볼륵을 남겨 두었을때 각각을 살펴봄.
/***************************************************************************************************************************/
CREATE OR REPLACE PROCEDURE DO_UPDATE (p_n IN NUMBER)
AS
pragma autonomous_transaction;
l_rec t%rowtype;
resource_busy exception;
pragma exception_init (resource_busy, -54);
BEGIN
SELECT *
INTO L_REC
FROM T
WHERE X = P_N
FOR UPDATE NOWAIT
;
DO_UPDATE(p_n + 1);
COMMIT;
EXCEPTION
WHEN RESOURCE_BUSY THEN
DBMS_OUTPUT.PUT_LINE('locked out trying to select now ' || p_n );
COMMIT;
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('we finished - no problem' );
COMMIT;
END;
/
Procedure created.
;
-- 4. 꽉 찬 블록 테스트
/***************************************************************************************************************************/
-- 38 로우에서 트랜잭션 슬롯이 소진되어 Lock을 수행할 수 없음.
/***************************************************************************************************************************/
EXEC DO_UPDATE(1)
;
locked out trying to select now 38
PL/SQL procedure successfully completed.
;
-- 5. 빈 블록 확보한 후 테스트
/***************************************************************************************************************************/
-- 블록에 46바이트 공간을 확보한 후 수행할 경우, 9 로우에 추가로 트랜잭션 슬롯 확보가 가능함.
/***************************************************************************************************************************/
TRUNCATE TABLE T
;
INSERT INTO T
SELECT ROWNUM,
RPAD('*', 147, '*')
FROM DUAL
CONNECT BY LEVEL <= 46
;
SELECT LENGTH(Y),
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) BLK,
COUNT(*),
MIN(X),
MAX(X)
FROM T
GROUP BY LENGTH(Y),
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
;
LENGTH(Y) BLK COUNT(*) MIN(X) MAX(X)
---------- ---------- ---------- ---------- ----------
147 1377886 46 1 46
;
EXEC DO_UPDATE(1)
;
we finished - no problem
PL/SQL procedure successfully completed.
;
-- 1. (Session_1) DEPT 테이블 UPDATE
UPDATE DEPT
SET DEPTNO = DEPTNO + 10
;
-- 2. (Session_2) DEPT 테이블 DROP
DROP TABLE DEPT
;
DROP TABLE DEPT
*
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified
;
-- 1. (Session_1) 샘플 테이블 생성
CREATE TABLE T1(X INT);
CREATE TABLE T2(X INT);
-- 2. (Session_2) 샘플 데이터 생성
INSERT INTO T1 VALUES(1);
INSERT INTO T2 VALUES(1);
-- 3. (Session_2) Lock 모니터링
/***************************************************************************************************************************/
-- TM Lock일 경우, L.ID1의 값은 OBJECT_ID와 동일하므로, Lock이 걸린 OBJECT_NAME을 찾을 수 있음.
/***************************************************************************************************************************/
SELECT (SELECT USERNAME
FROM V$SESSION
WHERE SID = L.SID) USERNAME,
L.SID,
L.ID1,
L.ID2,
L.LMODE,
L.REQUEST,
L.BLOCK,
L.TYPE
FROM V$LOCK L
WHERE L.SID = (SELECT SID
FROM V$MYSTAT
WHERE ROWNUM = 1)
;
USERNAME SID ID1 ID2 LMODE REQUEST BLOCK TYPE
----------- ---------- ---------- ---------- ---------- ---------- ---------- ----
APPS 9595 104970298 0 3 0 0 TM -- TM 락일 경우 ID1과 OBJECT_ID는 동일
APPS 9595 104970299 0 3 0 0 TM -- TM 락일 경우 ID1과 OBJECT_ID는 동일
APPS 9595 1367080983 3 6 0 0 TX
;
SELECT OBJECT_NAME,
OBJECT_ID
FROM USER_OBJECTS
WHERE OBJECT_ID IN (104970298, 104970299)
;
OBJECT_NAME OBJECT_ID
------------- ----------
T1 104970298
T2 104970299
;
BEGIN
COMMIT;
DDL-STATEMENT
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END;
구분 | 설명 |
---|---|
배타 DDL 락 | 다른 세션이 DDL 락 또는 TM 락을 획득하지 못하게 함. 이것은 DDL 작업을 하는 동안 테이블을 쿼리할 수 있지만, 어떤 방법으로든 테이블을 변경할 수 없다는 것을 의미함. |
공유 DDL 락 | 다른 세션에 의해 변경되는 것에 대비하여 참조하는 객체 구조를 보호하지만, 데이터에 대한 변경은 허용함. |
Breakable Parse 락 | shared pool에 게시된 실행계획처럼 객체 간의 의존 관계를 등록함. 만약, 특정 객체에 대해 DDL을 수행하면, 오라클은 객체 간의 의존 관계를 등록한 객체 리스트를 살펴보고, 의존 관계를 무효화시킨다. 그러므로, breakable parse 락은 깨지기 쉬우며, DDL이 발생하는 것을 막지 않음. |
ALTER TABLE T MOVE
;
Table altered.
;
-- 1. (Session_1) 샘플 데이터 생성
DROP TABLE T;
CREATE TABLE T AS
SELECT *
FROM ALL_OBJECTS
;
Table created.
;
SELECT OBJECT_ID
FROM USER_OBJECTS
WHERE OBJECT_NAME = 'T'
;
OBJECT_ID
----------
89221723
;
-- 2. (Session_1) ONLINE으로 인덱스 생성
CREATE INDEX T_N01 ON T(OWNER, OBJECT_TYPE, OBJECT_NAME) ONLINE
;
-- 3. (Session_2) Lock 모니터링
/***************************************************************************************************************************/
-- TM 락의 LMODE의 값은 로우 레벨인 2로 확인됨
-- DL(Direct load) 락은 인덱스 생성이 일어나는 동안, 대상 테이블에 direct path load를 막는 데 사용됨.
-- (즉, 테이블에 direct path load를 하면서, 동시에 인덱스를 생성할 수 없다는 것을 의미함)
-- 여기서는 10g 버전이라 나타나지 않았지만, 책에는 11g에서 추가된 OD 락이 설명되어 있다.
-- OD(Online DDL)락은 온라인 DDL을 허용하는데, 이전 버전에서는 완벽한 온라인 DDL이 아니었음.
-- (10g 이하 버전에서는 동시에 일어나는 작업을 막기 위해 CREATE 문의 시작과 끝에 락을 취함)
-- 11g 버전에서 OD 락은 완벽하게 온라인으로 작동하며, CREATE 문의 시작과 끝에 배타 락을 요구하지 않음.
/***************************************************************************************************************************/
SELECT (SELECT USERNAME
FROM V$SESSION
WHERE SID = L.SID) USERNAME,
L.SID,
L.ID1,
L.ID2,
L.LMODE,
L.REQUEST,
L.BLOCK,
L.TYPE
FROM V$LOCK L
WHERE L.ID1 = 89221723
;
USERNAME SID ID1 ID2 LMODE REQUEST BLOCK TYPE
--------- ---------- ---------- ---------- ---------- ---------- ---------- ----
APPS 19010 89221723 0 2 0 2 TM
APPS 19010 89221723 0 3 0 2 DL
APPS 19010 89221723 0 3 0 2 DL
;
COLUMN OWNER format a10
COLUMN NAME format a30
COLUMN TYPE format a30
COLUMN MODE_HELD format a9
COLUMN MODE_REQUESTED format a9
SELECT DDL.SESSION_ID SID,
DDL.OWNER,
DDL.NAME,
DDL.TYPE,
DDL.MODE_HELD HELD,
DDL.MODE_REQUESTED REQUEST
FROM DBA_DDL_LOCKS DDL
WHERE SESSION_ID = (SELECT SID
FROM V$MYSTAT
WHERE ROWNUM = 1)
;
SID OWNER NAME TYPE HELD REQUEST
---------- ---------- ------------------------------ ------------------------------ ------------------ ------------
19010 SYS XML_SCHEMA_NAME_PRESENT Table/Procedure/Type Null None
19010 XDB XDh1jUheYAR5PgNAgAILJCxg== 25 Share None
19010 SYS DBMS_SQL Table/Procedure/Type Null None
19010 XDB DBMS_XDBZ0 Body Null None
19010 SYS ORA_TQ_BASE$ Table/Procedure/Type Null None
19010 SYS XML_SCHEMA_NAME_PRESENT Body Null None
19010 XDB DBMS_XDBZ0 Table/Procedure/Type Null None
19010 SYS DBMS_STANDARD Table/Procedure/Type Null None
19010 XDB XDF5l4V627ZiTgQ4u5GGZmJA== 25 Share None
19010 XDB XDB$EXTNAME2INTNAME Table/Procedure/Type Null None
19010 SYS SYSEVENT Table/Procedure/Type Null None
19010 SYS PLITBLM Table/Procedure/Type Null None
19010 SYS DBMS_OUTPUT Body Null None
19010 XDB XDF5l4V6s9ZiTgQ4u5GGZmJA== 25 Share None
19010 SYS DICTIONARY_OBJ_NAME Table/Procedure/Type Null None
19010 SYS LOGIN_USER Table/Procedure/Type Null None
19010 SYS DBMS_APPLICATION_INFO Table/Procedure/Type Null None
19010 SYS DBMS_APPLICATION_INFO Body Null None
19010 SYS DICTIONARY_OBJ_OWNER Table/Procedure/Type Null None
19010 SYS DBMS_SYS_SQL Body Null None
19010 SYS DBMS_SYS_SQL Table/Procedure/Type Null None
19010 SYS DBMS_SQL Body Null None
19010 SYS DICTIONARY_OBJ_TYPE Table/Procedure/Type Null None
19010 XDB XDbD/PLZ01TcHgNAgAIIegtw== 25 Share None
19010 SYS DBMS_OUTPUT Table/Procedure/Type Null None
;
-- 1. 테스트 프로시저 생성 및 수행
CREATE OR REPLACE PROCEDURE P
AS
BEGIN
NULL;
END;
/
EXEC P
;
-- 2. 프로시저 P에 대한 내용을 DBA_DDL_LOCKS 뷰에서 확인
SELECT DDL.SESSION_ID SID,
DDL.OWNER,
DDL.NAME,
DDL.TYPE,
DDL.MODE_HELD HELD,
DDL.MODE_REQUESTED REQUEST
FROM DBA_DDL_LOCKS DDL
WHERE SESSION_ID = (SELECT SID
FROM V$MYSTAT
WHERE ROWNUM = 1)
;
SID OWNER NAME TYPE HELD REQUEST
---------- ---------- ------------------------------ ------------------------------ ------------------ ----------------
19010 SYS XML_SCHEMA_NAME_PRESENT Table/Procedure/Type Null None
19010 XDB XDh1jUheYAR5PgNAgAILJCxg== 25 Share None
19010 SYS DBMS_SQL Table/Procedure/Type Null None
19010 XDB DBMS_XDBZ0 Body Null None
19010 SYS ORA_TQ_BASE$ Table/Procedure/Type Null None
19010 SYS XML_SCHEMA_NAME_PRESENT Body Null None -- 대상
19010 USER P Table/Procedure/Type Null None
19010 XDB DBMS_XDBZ0 Table/Procedure/Type Null None
19010 XDB XDF5l4V627ZiTgQ4u5GGZmJA== 25 Share None
19010 XDB XDB$EXTNAME2INTNAME Table/Procedure/Type Null None
19010 SYS SYSEVENT Table/Procedure/Type Null None
19010 SYS PLITBLM Table/Procedure/Type Null None
19010 SYS DBMS_OUTPUT Body Null None
19010 SYS AW_DROP_PROC Table/Procedure/Type Null None
19010 XDB XDF5l4V6s9ZiTgQ4u5GGZmJA== 25 Share None
19010 SYS DICTIONARY_OBJ_NAME Table/Procedure/Type Null None
19010 SYS LOGIN_USER Table/Procedure/Type Null None
19010 SYS DBMS_APPLICATION_INFO Table/Procedure/Type Null None
19010 SYS DBMS_APPLICATION_INFO Body Null None
19010 SYS DICTIONARY_OBJ_OWNER Table/Procedure/Type Null None
19010 SYS DBMS_SYS_SQL Body Null None
19010 SYS DBMS_SYS_SQL Table/Procedure/Type Null None
19010 SYS DBMS_SQL Body Null None
19010 SYS DICTIONARY_OBJ_TYPE Table/Procedure/Type Null None
19010 XDB XDbD/PLZ01TcHgNAgAIIegtw== 25 Share None
19010 SYS IDGEN1$ Table/Procedure/Type Null None
19010 SYS DBMS_OUTPUT Table/Procedure/Type Null None
;
-- 3. 프로시저 재 컴파을 후 DBA_DDL_LOCKS 뷰 확인
/***************************************************************************************************************************/
-- 프로시저 컴파일을 통해 parse 락이 해제된 것 확인 가능.
-- 이와 같이, DBA_DDL_LOCKS 뷰는 일부 코드가 테스트 시스템 도는 개발 시스템에서 컴파일되지 않는다는 것을 발견할 때 유용함.
/***************************************************************************************************************************/
ALTER PROCEDURE P COMPILE;
SELECT DDL.SESSION_ID SID,
DDL.OWNER,
DDL.NAME,
DDL.TYPE,
DDL.MODE_HELD HELD,
DDL.MODE_REQUESTED REQUEST
FROM DBA_DDL_LOCKS DDL
WHERE SESSION_ID = (SELECT SID
FROM V$MYSTAT
WHERE ROWNUM = 1)
;
SID OWNER NAME TYPE HELD REQUEST
---------- ---------- ------------------------------ ------------------------------ ------------------ -------------
19010 SYS XML_SCHEMA_NAME_PRESENT Table/Procedure/Type Null None
19010 XDB XDh1jUheYAR5PgNAgAILJCxg== 25 Share None
19010 SYS DBMS_SQL Table/Procedure/Type Null None
19010 XDB DBMS_XDBZ0 Body Null None
19010 SYS ORA_TQ_BASE$ Table/Procedure/Type Null None
19010 SYS XML_SCHEMA_NAME_PRESENT Body Null None
19010 XDB DBMS_XDBZ0 Table/Procedure/Type Null None
19010 XDB XDF5l4V627ZiTgQ4u5GGZmJA== 25 Share None
19010 XDB XDB$EXTNAME2INTNAME Table/Procedure/Type Null None
19010 SYS SYSEVENT Table/Procedure/Type Null None
19010 SYS PLITBLM Table/Procedure/Type Null None
19010 SYS DBMS_OUTPUT Body Null None
19010 SYS AW_DROP_PROC Table/Procedure/Type Null None
19010 XDB XDF5l4V6s9ZiTgQ4u5GGZmJA== 25 Share None
19010 SYS DICTIONARY_OBJ_NAME Table/Procedure/Type Null None
19010 SYS LOGIN_USER Table/Procedure/Type Null None
19010 SYS DBMS_APPLICATION_INFO Table/Procedure/Type Null None
19010 SYS DBMS_APPLICATION_INFO Body Null None
19010 SYS DICTIONARY_OBJ_OWNER Table/Procedure/Type Null None
19010 SYS DBMS_SYS_SQL Body Null None
19010 SYS DBMS_SYS_SQL Table/Procedure/Type Null None
19010 SYS DBMS_SQL Body Null None
19010 SYS DICTIONARY_OBJ_TYPE Table/Procedure/Type Null None
19010 XDB XDbD/PLZ01TcHgNAgAIIegtw== 25 Share None
19010 SYS IDGEN1$ Table/Procedure/Type Null None
19010 SYS DBMS_OUTPUT Table/Procedure/Type Null None
;
1. Process A래치 획득 시도 > 해당 래치를 점유한 Process가 존재하지 않으므로 래치 획득
2. Process B 래치 획득 시도 > Process A가 래치를 점유하고 있으므로 래치 획득 실패
3. Process B는 _SPIN_COUNT의 수 만큼 스핀을 수행(스핀을 수행한 후 다시 래치 획득 시도. 기본값 : 2000) > spin으로 인한 CPU 사용률 증가
4. 만일 여러 번의 스핀을 반복한 후에도 래치를 획득하지 못할 경우, Process B는 Sleep 상태로 전환 > SPIN 행위 자체가 CPU를 사용하기 때문에, 래치 경합에 의해서 많은 수의 프로세스가 동시에 스핀을 수행할 경우 높은 cpu 사용율을 보일 수 있음.
LOOP
FOR i IN 1..2000 LOOP
try to get latch
if got latch, retuen
if i = 1 then misses = misses + 1
END LOOP;
INCREMENT WAIT COUNT
sleep
ADD WAIT TIME
END LOOP;
1. Process A 래치 획득 시도 > 해당 래치를 점유한 프로세스가 존재하지 않으므로 래치 획득
2. Process B 래치 획득 시도 > Process A가 래치를 점유하고 있으므로 래치 획득 실패
3. Process B를 대기목록(wait list)에 등록
4. Process A가 자원을 해제할 때 대기목록으로 알려 줌
5. 대기목록에서 대기하던 프로세스 중 하나가 래치를 획득
(순서보장을 하지 않음. 대기 목록에서 깨어난 프로세스가, 래치를 획득하려고 하는 그 찰나의 순간에, 신규로 래치를 획득하고자 하는 다른 프로세스가 래치를 획득 할 수 있기 때문)