본문 바로가기
Programming/Database System

[ORACLE] Lock 1편 - Lock의 개념과 매커니즘

by kghworks 2022. 10. 12.

 

 본 포스팅은 도서 "전문가를 위한 오라클 데이터베이스 아키텍처 (저:  토마스 카이트)" 중 chapter 06 락킹과 래칭을 참고하여 정리한 글입니다. 

 

 

 다중 사용자 기반의 애플리케이션이 데이터베이스를 사용한다면, 락킹 (locking) 메커니즘에 대해 고려하지 않을 수 없습니다.  락킹 메커니즘은 DB를 활용하는 주요 기능으로 개발자는 락킹 메커니즘을 충분히 이해하고 활용하여 애플리케이션을 개발해야 합니다. 특히 오라클의 경우 다른 DB보다 더 월등한 락킹 메커니즘을 제공합니다.

 Lock을 무시한 채 개발한 애플리케이션은 데이터 무결성이 깨지거나 예상 밖의 데이터 결과를 만들어 버리게 됩니다.  따라서 이번 포스팅에서는 공유 데이터 구조 (table, row, procedure 등)에 대하여 어떻게 락을 걸지 확인하고, 락의 원리까지 가볍게 이해해보겠습니다.

 

 

 그리고 락은 문제가 없습니다. 자원에 락을 거는 것은 데이터베이스가 공유자원에 대해 무결성을 유지하기 위한 아주 당연한 행위입니다. 부디 락을 충분히 이해하여 활용하셨으면 합니다.

 

목차

  • 락이란?
  • 오라클 Lock
  • 락 이슈 : LOST UPDATE
  • 비관적 락킹
  • 낙관적 락킹
  • 블로킹
  • 데드락
  • 참조

 


락  (Lock)이란?

 

 락 (Lock)은 데이터베이스 공유 자원에 대한 사용자들의 동시 액세스를 핸들링하기 위한 메커니즘입니다. 여기서 락을 거는 공유자원은 row, table, procedure 등 여러 수준입니다. 다수 사용자가 데이터를 변경할 때 같은 데이터를 액세스 하는 경우가 많기 때문에 정밀한 락킹 메커니즘을 가지는 것이 중요합니다. 그리고 오라클은 이미 그 메커니즘을 가지고 있습니다. 

 

단일 사용자만 있는 데이터베이스에 락이 필요할까.

 당연히 필요 없습니다. 데이터베이스를 변경하는 사용자가 한 명이기 때문입니다. 모든 자원이 공유자원이 아닌 단일 사용자만 접근하는 자원이며, 동시성을 제어하기 위한 락은 필요가 없습니다.

 

 트랜잭션 포스팅에서도 언급한 부분도 있지만 아래 항목들은 오라클 전체에서 통용되는 중요한 부분이니 다시 소개합니다.

 

  • 트랜잭션은 데이터베이스의 상태변경을 위한 논리적인 작업 단위이며 좋은 것. (트랜잭션에 대해 벌벌 떨지 마세요.)
  • 작업 도중 불안한 마음에 커밋하지말고, 적절한 순간까지 작업을 진행한 뒤 커밋할 것. 커밋은 해야할 때 하는 것.
  • 커밋을 미루는 것과 DDL 양이 시스템에 부하를 주지 않음.
  • 데이터 락은 필요시에 충분히 가지고 있어야 함. 본인이 해당 데이터를 액세스 하는 중임을 알리는 것이기 때문. 락은 피해야 하는 것이 아닌, 이해하고 활용해야 하는 것.
  • 오라클은 로우수준의 락에 대한 오버헤드 없음. 1개 로우에 대한 락이나 1만 개 로우에 대한 락이나 락에 사용되는 자원 수는 동일. (락을 거는 데 사용하는 자원은 고정 상수)
  • 락 에스컬레이션은 좋은 게 아님. 테이블 락은 배치 프로세스 정도에서나 활용성 있음. 로우락을 피하기 위해 테이블 락을 활용하려고 하지 말 것.
  • 오라클은 데이터를 쓰는자와 읽는 자를 서로 블로킹하지 않음.

오라클 Lock

 

 오라클은 DML 락, DDL 락, 래치 (Latch), 버퍼 락 등 다양한 락을 제공합니다. 애플리케이션 개발자 측에서 주의 깊게 봐야 하는 락은 DML 락 입니다.

 DML 락은 동시성을 구현과 동시에 데이터 무결성을 보호해줄 수 있습니다. 오라클 DML 락에는 로우 락테이블 락이 있습니다.

 

로우 락 (Row Lock)

 항상 배타적입니다. INSERT, UPDATE, DELETE, SELECT FOR UDPATE 문을 실행한 트랜잭션이 획득하는 락입니다. 이 락을 보유한 세션은 해당 트랜잭션을 종료하기 전까지 다른 트랜잭션은 해당 로우를 수정할 수 없습니다.

 SELECT 문은 어떠한 락도 설정하지 않습니다. 이 부분이 다른 DBMS와 대조되는 부분입니다. 오라클은 읽는 자와 쓰는 자를 서로 블로킹 (블로킹은 아래에서 설명) 하지 않도록 설계되어있습니다. 이는 아래와 같은 이점이 있습니다.

 

  • 읽으려고 하는 데이터가 다른 트랜잭션이 수정 중임에도 읽을 수 있음 (UNDO 데이터 활용)
  • 수정하려는 데이터를 다른 트랜잭션이 읽는 중이라도 기다리지 않음 (SELECT FOR UPDATE는 예외)
  • 수정하려는 데이터를 다른 트랜잭션이 수정 중이라면 기다림. (로우 락)

 

테이블 락 (Table Lock)

 로우락을 얻었다면 묵시적으로 동시에 테이블 락도 획득하게 됩니다. 다른 트랜잭션이 현재 수정 중인 테이블의 구조를 바꾸지 못하도록 (DDL 방지) 하기 위해서입니다. LOCK TABLE 명령어를 이용해서 명시적으로 테이블 락을 획득할 수도 있습니다.

 테이블 락의 종류와 각각의 호환성은 아래와 같습니다. 여기서 호환성은 락이 걸렸을 때 서로의 액션을 허용하느냐입니다.

 

  • Row Share (RS)
  • Row Exclusive (RX)
  • Share (S)
  • Share ROw Exclusive (SRX)
  • Exclusive (X)

테이블 락의 종류별 호환성

 


락 이슈 : LOST UPDATE

 

시나리오 하나를 보여드리겠습니다. 

 

  1. A 관리자 : 홍길동 회원 정보 변경 페이지 진입
  2. B 관리자 : 홍길동 회원 정보 변경 페이지 진입
  3. A 관리자 : 홍길동의 생년월일 정보 변경 후 저장 버튼 클릭
  4. A 관리자 : "정보 저장 완료!" 문구 확인 (COMMIT)
  5. B 관리자 : 홍길동의 주소 정보 변경 후 저장 버튼 클릭
  6. B 관리자 : "정보 저장 완료!" 문구 확인 (COMMIT)

결과 : A 관리자가 편집한 생년월일 정보가 덮어써짐 (생년월일 정보 변경이 반영되지 않음)

 

 위 현상을 lost update (갱신 손실)이라고 합니다. A 사용자의 UPDATE 결과가 사라져 버렸기 때문입니다. (분명히 저장 완료했다는 문구를 확인했음에도) 원인은 웹 페이지 수정 메뉴를 한 번이라도 개발해 보셨다면, 짐작 가능하실 겁니다. 상용서비스에서 적지 않게 겪는 부분이기도 하고요.

 

사건의 전말은 이렇습니다.

-- 1. A 관리자 : 홍길동 회원 정보 변경 페이지 진입
SELECT *
FROM TB_MEMBER
WHERE SEQ_MEM = '홍길동 PK';

-- 2. B 관리자 : 홍길동 회원 정보 변경 페이지 진입
SELECT *
FROM TB_MEMBER
WHERE SEQ_MEM = '홍길동 PK';


-- 3. A 관리자 : 홍길동의 생년월일 정보 변경 후 저장 버튼 클릭
-- 4. A 관리자 : "정보 저장 완료!" 문구 확인 (COMMIT)
UPDATE TB_MEMBER
SET (BIRTH, ADDR, NAME_MEM, ...)
VALUES (19950911, '서울시 서초구', '홍길동')
WHERE SEQ_MEM = '홍길동 PK';

COMMIT;

--홍길동의 생년월일이 19950911로 변경완료!

-- 5. B 관리자 : 홍길동의 주소 정보 변경 후 저장 버튼 클릭
-- 6. B 관리자 : "정보 저장 완료!" 문구 확인 (COMMIT)
UPDATE TB_MEMBER
SET (BIRTH, ADDR, NAME_MEM, ...)
VALUES (19920107, '대전광역시 유성구', '홍길동')
WHERE SEQ_MEM = '홍길동 PK';

COMMIT;

-- 홍길동의 생년월일을 다시 19920107 로 변경 완료! (ㅋㅋㅋㅋ)

 5번에서 UPDATE를 할 때 A 관리자의 변경 전의 생년월일 정보를 입력합니다. 왜냐하면 B 관리자는 A관리자가 생년월일 정보를 변경 (COMMIT) 하기 전에 이미 SELECT 해서 화면에 정보를 제공했기 때문이죠. 

 A와 B관리자가 동시에 페이지에 진입하지 않아도 발생할 수 있습니다. A관리자가 변경 페이지에 진입한 뒤 30분이 지나고 B관리자가 진입했어도 위 현상은 발생할 수 있습니다. (WAS 세션 유지 시간은 고려 X) 

 

 어떻게 막아야 할까요. 그 ROW에 LOCK을 걸어 다른 사람이 동시에 변경하지 못하게 하는 건 어떤가요?

 

-- 1. A 관리자 : 홍길동 회원 정보 변경 페이지 진입
SELECT *
FROM TB_MEMBER
WHERE SEQ_MEM = '홍길동 PK';

-- 2. B 관리자 : 홍길동 회원 정보 변경 페이지 진입
-- 하려했으나!! "홍길동 회원을 다른 사용자가 수정 중입니다! 잠시 후 접속하세요." ALERT

 

 사용자가 한두 명도 아니고, 많은 사용자들이 동시에 공유자원 (홍길동 ROW)을 액세스 할 겁니다. 위 방법은 애플리케이션의 사용성이 너무 떨어집니다. 이제 LOCK 메커니즘을 배워 활용해보겠습니다.

 


비관적 락킹

 

 이 방법은 비추입니다. 1차원적이고 원시적인 락킹 메커니즘으로 과거에 쓰이던 방식입니다. 사용자가 해당 row에 대해 변경 의사를 가질 때 (=SELECT  할 때, 변경 페이지 진입 시) 즉시 로우에 락을 거는 방법입니다. 위에서 A 관리자가 홍길동 정보 수정 페이지 진입을 위해 SELECT 하는 즉시 ROW에 락을 겁니다. 그리고 이 LOCK은 애플리케이션이 로우를 변경하고 커밋할 때까지 계속됩니다. 그렇기 때문에 이 방법은 커넥션이 유지되는 환경에서만 유효합니다. 용자가 락을 건 뒤 커넥션을 끊어버리면 끔찍한 상황이 시작됩니다..

 

 불안한데요. 아무튼 방법을 살펴보겠습니다. 비관적 락킹은 내가 SELECT 할 때 지금 락을 걸지 않으면 누군가가 바꾸지 않을까 의심하기 때문에 SELECT 할 때 LOCK을 겁니다. 

 

SELECT FOR UPDATENOWAIT 옵션을 주어보겠습니다.

 

* SELECT FOR UDPATE NOWAIT : SELECT와 동시에 해당 행에 LOCK을 내가 보유하겠다. 만일 다른 세션이 이미 ROW에 LOCK을 보유 중이라면 기다리지 않고 SELECT를 포기하겠다.

 

-- 1. A 관리자 : 홍길동 회원 정보 변경 페이지 진입
SELECT *
FROM TB_MEMBER
WHERE SEQ_MEM = '홍길동 PK'
FOR UPDATE NOWAIT;

-- 2. B 관리자 : 홍길동 회원 정보 변경 페이지 진입
SELECT *
FROM TB_MEMBER
WHERE SEQ_MEM = '홍길동 PK'
FOR UPDATE NOWAIT;
-- ORA-00054 resource busy 발생
-- B관리자는 해당 리소스를 수정할 수 없음!

-- 3. A 관리자 : 홍길동의 생년월일 정보 변경 후 저장 버튼 클릭
-- 4. A 관리자 : "정보 저장 완료!" 문구 확인 (COMMIT)
UPDATE TB_MEMBER
SET (BIRTH, ADDR, NAME_MEM, ...)
VALUES (19950911, '서울시 서초구', '홍길동')
WHERE SEQ_MEM = '홍길동 PK';

COMMIT;

-- 5. B 관리자 : 홍길동 회원 정보 변경 페이지 다시 진입
SELECT *
FROM TB_MEMBER
WHERE SEQ_MEM = '홍길동 PK'
FOR UPDATE NOWAIT;

-- 6. B 관리자 : 홍길동의 주소 정보 변경 후 저장 버튼 클릭
-- 7. B 관리자 : "정보 저장 완료!" 문구 확인 (COMMIT)
UPDATE TB_MEMBER
SET (BIRTH, ADDR, NAME_MEM, ...)
VALUES (19950911, '대전광역시 유성구', '홍길동')
WHERE SEQ_MEM = '홍길동 PK';

COMMIT;

 

 위처럼 비관적 락킹을 이용할 때 사용자는 아래 3가지 중 하나의 결과를 받을 수 있습니다.

 

  • 정보 수정 페이지 진입과 동시에 홍길동 로우에 락을 걸어 다른 세션이 수정 못하도록 막음.
  • 다른 세션이 홍길동 정보를 수정하는 중 (=이미 LOCK을 보유)이라면 ORA-00054 resource busy 발생. 다른 사용자가 수정 완료 (COMMIT)할 때까지 기다려야 함
  • 락을 성공적으로 획득 후 COMMIT 하려 할 때 다른 세션이 로우를 수정했다면 0건의 로우를 리턴.  A관리자가 최초에 받은 정보는 이미 최신 정보가 아니라는 것을 의미 (LOST UPDATE 방지)

 

이렇게 SELECT FOR UPDATE를 이용하여 비관적 락킹을 구현할 수 있습니다. 

 

정리

  • 비관적 락킹이란 내가 수정하기 전에 다른 사용자가 수정하진 않을까 하는 비관적인 태도를 가지고 SELECT와 동시에 락을 거는 것
  • 락은 사용자가 수정 완료 (COMMIT)할 때까지 계속됨으로 커넥션이 계속 유지되는 애플리케이션에서 활용성 있음
  • 옛날 방식. 비추

 


낙관적 락킹 (추천)

 

 추천하는 메커니즘입니다. 수정하기 직전까지 락을 미루는 방식입니다. 다른 사용자가 데이터를 수정하지 않을 것이라는 낙관적인 태도로 락을 이용합니다. 그러나 수정이 실패될 확률이 비관적 락킹에 비해 높습니다.

 

버전 컬럼 (version column)

낙관적 락킹을 구현할 때는 버전 컬럼 (version column)을 이용합니다. 버전 컬럼은 컬럼으로서 해당 row에 수정이 될 때마다 날짜 (혹은 시퀀스 넘버)를 넣어서 해당 로우의 버전을 알 수 있도록 하는 것입니다. 예를 들면 아래와 같습니다.

 

-- DT_UPDT 컬럼을 버전 컬럼으로 활용
-- 로우에 수정이 있을때 마다 SYSDATE를 넣어줌

-- 홍길동의 주소 데이터를 변경
UPDATE TB_MEMBER
SET (ADDR, DT_UPDT)
VALUES ('대전광역시 유성구', SYSDATE)
WHERE SEQ_MEM = '홍길동 PK';

 

이제 버전 컬럼을 이용한 낙관적 락킹 시나리오를 보겠습니다.

-- 일시 : 8시
-- 1. A 관리자 : 홍길동 회원 정보 변경 페이지 진입
SELECT *
FROM TB_MEMBER
WHERE SEQ_MEM = '홍길동 PK';

-- 일시 :  8시 1분
-- 2. B 관리자 : 홍길동 회원 정보 변경 페이지 진입
SELECT *
FROM TB_MEMBER
WHERE SEQ_MEM = '홍길동 PK';


-- 일시 : 8시 2분
-- 3. A 관리자 : 홍길동의 생년월일 정보 변경 후 저장 버튼 클릭
-- 4. A 관리자 : "정보 저장 완료!" 문구 확인 (COMMIT)

UPDATE TB_MEMBER
SET (BIRTH, ADDR, NAME_MEM, ...,DT_UPDT)
VALUES (19950911, '서울시 서초구', '홍길동', ..., SYSDATE)
WHERE SEQ_MEM = '홍길동 PK'
        AND DT_UPDT = '기존 DT_UPDT';

COMMIT;

-- 일시 : 8시 3분
-- 5. B 관리자 : 홍길동의 주소 정보 변경 후 저장 버튼 클릭
UPDATE TB_MEMBER
SET (BIRTH, ADDR, NAME_MEM, ...,DT_UPDT)
VALUES (19950911, '서울시 서초구', '홍길동', ..., SYSDATE)
WHERE SEQ_MEM = '홍길동 PK'
        AND DT_UPDT = '기존 DT_UPDT';

COMMIT;
-- COMMIT 실패! UPDATE ROW : 0건

 

 

 B관리자는 자신의 UDPATE가 실패했으나 LOST UPDATE가 발생하지 않았습니다. 5번의 UDPATE 문 WHERE 절에서 버전컬럼은 이미 예전데이터이기 때문입니다.

 

버전 컬럼 값의 관리

 버전 컬럼의 데이터 타입은 NUMBER 타입, DATE 타입, TIMESTAMP 타입 정도가 적당하고, 그중 TIMESTAMP를 추천합니다. TIMESTAMP는 오라클에서 가장 정밀도가 높은 데이터 타입으로 마이크로세컨드까지 표현이 가능하기 때문에 애플리케이션 수준에서 충분한 정밀도를 제공할 수 있습니다.

 

 버전 컬럼 타입이 날짜형이라면 최신 날짜로 유지하는 방안이 필요합니다. 먼저 로우 트리거 (trigger)를 이용해서 update 시마다 유지하는 방안이 있습니다. 트리거를 이용하면 데이터베이스 측에서 강제로 최신 값을 유지하기 때문에 확실한 방법이 될 수 있겠으나, 트리거 실행에 대한 오버헤드도 상당량이 있기 때문에 트리거 사용을 자제해야 합니다.

 따라서 애플리케이션에서 udpate마다 컬럼을 최신 날짜로 할당해주는 것이 좋습니다. 개발자라면 매 update문마다 버전 컬럼을 최신으로 유지하는 것을 애플리케이션 단에 의존하지 않으려고 할 것입니다. 중복 코드이기 때문입니다.

 대안으로 저장 프로시저 (procedure)를 이용하여, 버전 컬럼이 있는 테이블에 대한 수정 비즈니스를 한 곳으로 통일하는 방법도 있습니다. 이러면 테이블에 대한 수정을 프로시저 외에 일절 허용하지 않는 효과도 볼 수 있습니다.

 

 개인적인 생각으로는 UPDATE 마다 애플리케이션 단에서 버전 컬럼을 최신 값으로 유지해주는 것을 추천하며 DB 정책, DBA의 스타일에 따라 프로시저를 활용하여 수정 단을 하나로 통일하는 방법도 고려해봤으면 합니다. TIMESTAMP 타입의 버전 컬럼은 해당 로우에 마지막 수정 일시 정보를 제공해주는 부가적인 이점도 있습니다.


블로킹 (blocking)

 

 블로킹 (blocking)은 요구한 자원의 락을 이미 다른 세션이 보유하고 있을 때 락을 보유한 세션이 자원을 포기할 때까지 진행되지 못하고 멈추게 되는 것 (=기다림)을 말합니다. (block 당한 사용자는 쿼리가 계속 실행중인 것처럼 보이게 됩니다)

 

blocking

 

 위 화면에서 session 14 접속자는 T3에 block 당한 상태로 쿼리가 계속 실행중인 것처럼 보이게 됩니다. 실제로는 session 15가 해당 row의 자원을 포기하기를 기다리고 있습니다.

 

-- session A 
-- session A : 홍길동 상세페이지 진입
SELECT *
FROM TB_USER
WHERE SEQ_MEM = '홍길동 PK'
FOR UPDATE;

-- session B
SELECT *
FROM TB_USER
WHERE SEQ_MEM = '홍길동 PK'
    FOR UPDATE;
-- block! querying ...

 

 데이터베이스에서 블로킹되는 DML 문은 INSERT, UPDATE, DELETE, MERGE, SELECT FOR UPDATE 문 5개입니다.

그중 SELECT FOR UPDATE는 NOWAIT 옵션으로 간단하게 해결할 수 있습니다. (아래 쿼리 참고)

 

-- session A
-- session A : 홍길동 상세페이지 진입
SELECT *
FROM TB_USER
WHERE SEQ_MEM = '홍길동 PK'
FOR UPDATE;

-- session B
SELECT *
FROM TB_USER
WHERE SEQ_MEM = '홍길동 PK'
    FOR UPDATE NOWAIT;
-- ORA-00054 resource busy 발생

 

남은 4개의 DML문에서 발생하는 블로킹에 대해 알아보겠습니다.

 

INSERT 블로킹

 대부분 PK, UNIQUE 제약조건이 있는 컬럼에게 중복 값을 넣으려는 서로 다른 세션들이 있을 때 발생합니다. (아래 쿼리 참고)

 

-- session A : 홍길동 생성
INSERT INTO TB_USER
SET (SEQ_MEM, NAME_MEM, ...)
    VALUES (1, '홍길동', ...);


-- session B : 세종대왕 생성
INSERT INTO TB_USER
SET (SEQ_MEM, NAME_MEM, ...)
    VALUES (1, '세종대왕', ...);
-- block! querying ...

 

 위 경우 session A 가 commit 하지 않아도 B가 블로킹됩니다.  참조 무결성 제약조건 (*FK)이 걸린 컬럼에 대해서도 발생할 수 있습니다. 

-- session A : 부서 생성
INSERT INTO TB_DEPT
SET (SEQ_MEM, NAME_DEPT, ...)
    VALUES (136, '개발팀', ...);


-- session B : 세종대왕 생성
INSERT INTO TB_USER
SET (SEQ_MEM, NAME_MEM, ...)
    VALUES (1, '홍길동', ...);
-- block! querying ...

 

 INSERT 블로킹은 시퀀스나 SYS_GUID() 내장 함수를 사용하여 블로킹을 피할 수 있습니다. 특히 시퀀스의 경우 UNIQUE 데이터를 만들 때 높은 동시성을 제공할 수 있도록 설계되어있습니다.

 만약 두 가지 모두 사용할 수 없는 상황이라면 DBMS_LOCK 패키지를 사용하여 수동 락을 구현하면 되는데 이는 상당한 오버헤드를 초래하므로 DBMS_LOCK 패키지를 사용해야만 한다면 근본적으로 애플리케이션 아키텍처 자체를 재설계하는 것을 추천하는 바입니다. 

 

MERGE, UDPATE, DELETE 블로킹

 이 때는 대부분이 lost update 문제를 안고 있는 경우입니다. 이때는 낙관적 / 비관적 락킹을 이용하여 블로킹을 피하면 됩니다. 이 둘에 대해서는 위에서 충분히 설명하였습니다.

 

 

 


데드락

 데드락 (dead lock)은 두 세션이 서로가 원하는 자원에 락을 보유하고 있을 때 발생합니다.

데드락은 우선 둘 이상의 세션에서 한쪽이 블로킹 당하는 상황에서 시작됩니다. 데드락이 발생하면 새롭게 진입한 세션이 블로킹을 당하고, 다른 세션은 블로킹 당하던 것이 취소되고 rollback 됩니다. 이 때 데드락을 유발한 해당 문장만 rollback 됩니다.

 session 14 가 블로킹되어 기다리는 상황에서 sesion 15가 요구하는 즉시 sesison 14는 데드락이 발동되고 rollback 됩니다. 그리고 T2에 session 14가 로우에 락을 보유했기 떄문에 T4에 sesion 15는 블로킹 됩니다.

 

T3 : session 14 blocking

 

T4 : session 15 blocking, session 14 dead lock 감지

 

T4 : ORA-00060 : Deadlock Detected session 14

 

 즉 오라클은 블로킹을 통해 데이터를 보호하고, 두 개 이상의 세션에서 서로가 보유한 자원을 요구한다면 데드락을 발동시켜, 다시 블로킹하는 상황을 만들어 보호를 계속합니다. (오라클 기준)

 

 


참조

 

전문가를 위한 오라클 데이터베이스 아키텍처 (저 : 토마스 카이트)

http://www.yes24.com/Product/Goods/5239961

 

전문가를 위한 오라클 데이터베이스 아키텍처 - YES24

오라클의 대부로 불리는 토마스 카이트는 오라클 데이터베이스와 아키텍처에 관한 모든 것을 이 책에 담고 있다. 이 책은 독자가 ODBC를 이용하는 Visual Basic 프로그래머이거나, EJB와 JDBC를 이용하

www.yes24.com

https://support.oracle.com/knowledge/Oracle%20Database%20Products/62365_1.html

 

Troubleshooting "ORA-00060 Deadlock Detected" Errors

Troubleshooting "ORA-00060 Deadlock Detected" Errors (Doc ID 62365.1) Last updated on MARCH 17, 2022 Applies to: Oracle Database - Enterprise Edition - Version 7.0.16.0 and later Oracle Database - Personal Edition - Version 7.1.4.0 and later Oracle Databas

support.oracle.com

 

댓글