[오라클] Oracle® Database Concepts 챕터 9 한글 번역

torch·2024년 7월 22일
0

Oracle

목록 보기
9/13
post-thumbnail

Reference : 9. Data Concurrency and Consistency

*GPT 번역 기반으로 내용이 정확하지 않을 수 있습니다.


Data Concurrency and Consistency

이 장에서는 다중 사용자 데이터베이스 환경에서 Oracle 데이터베이스가 일관된 데이터를 유지하는 방법을 설명합니다.

이 장은 다음 섹션을 포함합니다:

  • 데이터 동시성 및 일관성 소개
  • Oracle 데이터베이스 트랜잭션 격리 수준 개요
  • Oracle 데이터베이스 잠금 메커니즘 개요
  • 자동 잠금 개요
  • 수동 데이터 잠금 개요
  • 사용자 정의 잠금 개요

Introduction to Data Concurrency and Consistency

단일 사용자 데이터베이스에서는 사용자가 다른 사용자가 동일한 데이터를 동시에 수정하는 것에 대해 걱정할 필요 없이 데이터를 수정할 수 있습니다. 그러나 다중 사용자 데이터베이스에서는 여러 동시 트랜잭션 내에서 동일한 데이터를 업데이트할 수 있습니다. 동시에 실행되는 트랜잭션은 의미 있고 일관된 결과를 생성해야 합니다.

다중 사용자 데이터베이스는 다음을 제공해야 합니다:

  • 사용자가 동시에 데이터에 액세스할 수 있다는 보장(데이터 동시성)
  • 각 사용자가 데이터의 일관된 보기를 볼 수 있다는 보장(데이터 일관성), 여기에는 사용자의 자체 트랜잭션과 다른 사용자의 커밋된 트랜잭션에 의해 이루어진 변경 사항이 포함됩니다.

동시 실행되는 트랜잭션의 일관된 트랜잭션 동작을 설명하기 위해 데이터베이스 연구자들은 직렬화 가능성이라는 트랜잭션 격리 모델을 정의했습니다. 직렬화 가능한 트랜잭션은 다른 사용자가 데이터베이스의 데이터를 수정하지 않는 것처럼 보이는 환경에서 작동합니다.

이 정도의 격리는 일반적으로 바람직하지만, 직렬화 모드에서 많은 응용 프로그램을 실행하면 응용 프로그램 처리량이 심각하게 저하될 수 있습니다. 동시에 실행되는 트랜잭션을 완전히 격리하면 한 트랜잭션이 다른 트랜잭션이 쿼리하고 있는 테이블에 삽입을 수행할 수 없음을 의미할 수 있습니다. 요컨대, 현실적인 고려 사항은 완벽한 트랜잭션 격리와 성능 사이의 타협을 보통 요구합니다.

Oracle 데이터베이스는 다중 버전 일관성 모델과 다양한 유형의 잠금 및 트랜잭션을 사용하여 데이터 일관성을 유지합니다. 이렇게 하면 데이터베이스는 여러 동시 사용자에게 특정 시점까지 일관된 데이터를 제공합니다. 동시에 서로 다른 데이터 블록 버전이 존재할 수 있으므로, 트랜잭션은 쿼리가 필요로 하는 시점에 커밋된 데이터 버전을 읽고 단일 시점에 일관된 결과를 반환할 수 있습니다.

참조:

"데이터 무결성" 및 "트랜잭션"

Multiversion Read Consistency

Oracle 데이터베이스에서 다중 버전화는 여러 버전의 데이터를 동시에 실현하는 기능입니다. Oracle 데이터베이스는 다중 버전 읽기 일관성을 유지합니다.

Oracle 데이터베이스 쿼리의 특징은 다음과 같습니다:

  • Read-consistent queries

쿼리에 의해 반환된 데이터는 단일 시점에 대해 커밋되고 일관된 데이터입니다.

참고: Oracle 데이터베이스는 다른 트랜잭션에서 커밋되지 않은 데이터를 읽는 것을 결코 허용하지 않습니다(더티 읽기).

더티 읽기의 문제를 설명하기 위해, 하나의 트랜잭션이 컬럼 값을 커밋하지 않고 업데이트한다고 가정해 보겠습니다. 두 번째 트랜잭션이 업데이트된 더티(커밋되지 않은) 값을 읽습니다. 첫 번째 세션이 트랜잭션을 롤백하여 컬럼이 이전 값을 갖게 하지만, 두 번째 트랜잭션은 업데이트된 값을 사용하여 계속 진행하여 데이터베이스를 손상시킵니다. 더티 읽기는 데이터 무결성을 손상시키고, 외래 키를 위반하며, 고유 제약 조건을 무시합니다.

  • 비차단 쿼리

데이터의 읽기와 쓰기는 서로 차단되지 않습니다.

참조: "잠금 동작 요약"

Statement-Level Read Consistency

Oracle 데이터베이스는 항상 문 수준 읽기 일관성을 강제하며, 이는 단일 쿼리에 의해 반환된 데이터가 단일 시점에 대해 커밋되고 일관된 데이터를 보장합니다.

단일 SQL 문이 일관된 시점은 트랜잭션 격리 수준과 쿼리의 성격에 따라 다릅니다:

  • 읽기 커밋된 격리 수준에서는 이 시점이 문이 열린 시간입니다. 예를 들어, SELECT 문이 SCN 1000에서 열리면 이 문은 SCN 1000에 일관됩니다.
  • 직렬화 가능 또는 읽기 전용 트랜잭션에서는 이 시점이 트랜잭션이 시작된 시간입니다. 예를 들어, 트랜잭션이 SCN 1000에서 시작되면 이 트랜잭션 내의 여러 SELECT 문은 각각 SCN 1000에 일관됩니다.
  • 플래시백 쿼리 작업(SELECT ... AS OF)에서는 SELECT 문이 명시적으로 시점을 지정합니다. 예를 들어, 지난 목요일 오후 2시에 테이블을 조회할 수 있습니다.

참조: Oracle 데이터베이스 개발 가이드에서 플래시백 쿼리에 대해 배우기

Transaction-Level Read Consistency

Oracle 데이터베이스는 또한 트랜잭션 내의 모든 쿼리에 대해 읽기 일관성을 제공할 수 있으며, 이를 트랜잭션 수준 읽기 일관성이라고 합니다.

이 경우 트랜잭션의 각 문은 동일한 시점의 데이터를 봅니다. 이는 트랜잭션이 시작된 시점입니다.

직렬화 가능한 트랜잭션에 의해 실행된 쿼리는 트랜잭션 자체에 의해 이루어진 변경 사항을 봅니다. 예를 들어, 직원 정보를 업데이트한 트랜잭션이 다시 직원 정보를 조회하면 업데이트된 내용을 봅니다. 트랜잭션 수준 읽기 일관성은 반복 가능한 읽기를 생성하며, 쿼리를 유령 읽기(phantom read)에 노출시키지 않습니다.

Read Consistency and Undo Segments

다중 버전 읽기 일관성 모델을 관리하기 위해 데이터베이스는 테이블이 동시에 조회되고 업데이트될 때 읽기 일관된 데이터 집합을 생성해야 합니다.

Oracle 데이터베이스는 undo 데이터를 통해 읽기 일관성을 달성합니다.

사용자가 데이터를 수정할 때마다 Oracle 데이터베이스는 undo 항목을 생성하여 이를 undo 세그먼트에 기록합니다. undo 세그먼트에는 커밋되지 않았거나 최근에 커밋된 트랜잭션에 의해 변경된 데이터의 이전 값이 포함됩니다. 따라서 데이터베이스에는 서로 다른 시점의 동일한 데이터의 여러 버전이 존재할 수 있습니다. 데이터베이스는 서로 다른 시점의 데이터 스냅샷을 사용하여 읽기 일관된 데이터를 제공하고 비차단 쿼리를 가능하게 합니다.

읽기 일관성은 단일 인스턴스 및 Oracle Real Application Clusters(Oracle RAC) 환경에서 보장됩니다. Oracle RAC는 캐시 융합(cache fusion)으로 알려진 캐시 간 블록 전송 메커니즘을 사용하여 데이터 블록의 읽기 일관된 이미지를 한 데이터베이스 인스턴스에서 다른 인스턴스로 전송합니다.

참조: "Undo 세그먼트"에서 undo 저장소에 대해 배우기
"내부 LOB"에서 LOB의 읽기 일관성 메커니즘에 대해 배우기

Read Consistency: Example

이 예는 읽기 커밋된 격리 수준에서 문 수준 읽기 일관성을 제공하기 위해 undo 데이터를 사용하는 쿼리를 보여줍니다.

그림 9-1 읽기 커밋된 격리 수준에서의 읽기 일관성

그림 9-1의 설명
그림 9-1 "읽기 커밋된 격리 수준에서의 읽기 일관성"의 설명

데이터베이스가 쿼리를 위해 데이터 블록을 검색할 때, 데이터베이스는 각 블록의 데이터가 쿼리가 시작된 시점의 내용을 반영하도록 합니다. 데이터베이스는 필요한 경우 블록에 대한 변경 사항을 롤백하여 쿼리가 시작된 시점까지 블록을 재구성합니다.

데이터베이스는 SCN이라는 내부 정렬 메커니즘을 사용하여 트랜잭션의 순서를 보장합니다. SELECT 문이 실행 단계에 들어갈 때, 데이터베이스는 쿼리가 실행을 시작했을 때 기록된 SCN을 결정합니다. 그림 9-1에서 이 SCN은 10023입니다. 쿼리는 SCN 10023에 따라 커밋된 데이터만을 봅니다.

그림 9-1에서 SCN 10023 이후의 블록은 변경된 데이터를 나타내며, 이는 SCN 10024를 가진

두 블록으로 나타납니다. SELECT 문은 커밋된 변경 사항과 일관된 블록 버전을 필요로 합니다. 데이터베이스는 현재 데이터 블록을 새로운 버퍼로 복사하고 undo 데이터를 적용하여 블록의 이전 버전을 재구성합니다. 이러한 재구성된 데이터 블록을 일관된 읽기(CR) 클론이라고 합니다.

그림 9-1에서 데이터베이스는 두 개의 CR 클론을 생성합니다: 하나는 SCN 10006에 일관된 블록이고, 다른 하나는 SCN 10021에 일관된 블록입니다. 데이터베이스는 쿼리를 위해 재구성된 데이터를 반환합니다. 이렇게 하여 Oracle 데이터베이스는 더티 읽기를 방지합니다.

참조: "데이터베이스 버퍼 캐시" 및 "시스템 변경 번호(SCN)"에서 추가 정보

Read Consistency and Interested Transaction Lists

각 세그먼트 블록의 블록 헤더에는 관심 트랜잭션 목록(ITL)이 포함되어 있습니다.

데이터베이스는 ITL을 사용하여 데이터베이스가 블록을 수정하기 시작했을 때 트랜잭션이 커밋되지 않았는지 여부를 결정합니다.

ITL의 항목은 트랜잭션이 잠금한 행과 블록 내에서 커밋된 변경 사항 및 커밋되지 않은 변경 사항을 포함하는 행을 설명합니다. ITL은 undo 세그먼트의 트랜잭션 테이블을 가리키며, 이 테이블은 데이터베이스에 대한 변경 사항의 시점에 대한 정보를 제공합니다.

어떤 의미에서는 블록 헤더가 블록 내 각 행에 영향을 미친 트랜잭션의 최근 기록을 포함합니다. CREATE TABLE 및 ALTER TABLE 문에서 INITRANS 매개변수는 유지되는 트랜잭션 기록의 양을 제어합니다.

참조: INITRANS 매개변수에 대해 배우려면 Oracle 데이터베이스 SQL 언어 참조를 참조하십시오.

Read Consistency and Deferred Inserts

지연 삽입으로 알려진 특수 유형의 삽입은 표준 읽기 일관성 메커니즘을 사용하지 않습니다.

지연 삽입은 MEMOPTIMIZE_FOR_WRITE 힌트를 사용하여 MEMOPTIMIZE_FOR_WRITE로 지정된 테이블에 삽입합니다. 데이터베이스는 이러한 삽입을 버퍼 캐시가 아닌 대용량 풀에 버퍼링합니다. 데이터베이스는 redo와 undo를 사용하여 변경 사항을 추적하지 않습니다. 대신, 데이터베이스는 스페이스 관리 코디네이터(SMCO)가 버퍼를 디스크에 기록할 때 변경 사항을 자동으로 커밋합니다. 변경 사항은 롤백할 수 없습니다.

지연 삽입은 기존 삽입과 중요한 방식에서 다릅니다:

  • 애플리케이션에서 커밋된 것으로 가정하는 대용량 풀에 있는 데이터는 손실될 수 있습니다. 예를 들어, 데이터베이스 인스턴스가 변경 사항이 디스크에 저장되기 전에 실패할 수 있으며, 애플리케이션은 변경 사항이 저장되었다고 보고합니다.
  • 메모리에서 직접 데이터를 읽는 것은 허용되지 않습니다. 작성자는 자신의 변경 사항을 백그라운드 프로세스가 디스크에 기록할 때까지 읽을 수 없습니다. 어떤 읽기 작업도 변경 사항이 디스크에 기록될 때까지 커밋된 변경 사항을 볼 수 없습니다.
  • 데이터 손실을 방지해야 하는 클라이언트 애플리케이션은 대용량 풀에 기록한 후 로컬 복사본을 유지해야 합니다. 클라이언트는 DBMS_MEMOPTIMIZE 패키지를 사용하여 메모리에 기록된 내구성을 추적하고, DBMS_MEMOPTIMIZE_ADMIN 패키지를 사용하여 데이터베이스가 디스크에 기록하도록 강제할 수 있습니다.

참조: 지연 삽입에 대한 대용량 풀 버퍼에 대해 배우려면 관련 문서를 참조하십시오.
Oracle 데이터베이스 SQL 언어 참조에서 MEMOPTIMIZE_FOR_WRITE 힌트에 대해 자세히 배우기

Locking Mechanisms

일반적으로 다중 사용자 데이터베이스는 데이터 동시성, 일관성 및 무결성 문제를 해결하기 위해 일부 형태의 데이터 잠금을 사용합니다.

잠금은 동일한 리소스에 액세스하는 트랜잭션 간의 파괴적인 상호작용을 방지하는 메커니즘입니다.

참조: "Oracle 데이터베이스 잠금 메커니즘 개요"

ANSI/ISO Transaction Isolation Levels

SQL 표준은 ANSI와 ISO/IEC에 의해 채택된 네 가지 수준의 트랜잭션 격리 수준을 정의합니다. 이러한 수준은 트랜잭션 처리량에 대한 영향도가 다릅니다.

이 격리 수준은 동시에 실행되는 트랜잭션 간에 방지해야 할 현상(phenomena)으로 정의됩니다. 방지 가능한 현상은 다음과 같습니다:

  • 더티 읽기
    • 하나의 트랜잭션이 아직 커밋되지 않은 다른 트랜잭션이 기록한 데이터를 읽습니다.
  • 비반복적(불명확한) 읽기
    • 하나의 트랜잭션이 이전에 읽은 데이터를 다시 읽고 다른 커밋된 트랜잭션이 데이터를 수정하거나 삭제했음을 발견합니다. 예를 들어, 사용자가 행을 조회하고 나중에 동일한 행을 다시 조회하지만 데이터가 변경되었음을 발견합니다.
  • 유령 읽기
    • 하나의 트랜잭션이 특정 조건을 만족하는 행 집합을 반환하는 쿼리를 다시 실행하고, 다른 커밋된 트랜잭션이 조건을 만족하는 추가 행을 삽입했음을 발견합니다. 예를 들어, 트랜잭션이 직원 수를 조회합니다. 5분 후 동일한 조회를 수행하지만, 다른 사용자가 새로운 직원 기록을 삽입했기 때문에 직원 수가 하나 증가했습니다. 쿼리 기준을 만족하는 데이터가 더 많아졌지만, 이전에 읽은 데이터는 변경되지 않았습니다.

SQL 표준은 특정 격리 수준에서 실행되는 트랜잭션이 경험할 수 있는 현상으로 격리 수준을 정의합니다. 표 9-1은 격리 수준에 따른 방지 가능한 읽기 현상을 보여줍니다.

격리 수준더티 읽기비반복적 읽기유령 읽기
읽기 미커밋가능가능가능
읽기 커밋불가능가능가능
반복 가능한 읽기불가능불가능가능
직렬화 가능불가능불가능불가능

Oracle 데이터베이스는 읽기 커밋(기본값) 및 직렬화 가능한 격리 수준을 제공합니다. 또한 데이터베이스는 읽기 전용 모드를 제공합니다.

참조: "Oracle 데이터베이스 트랜잭션 격리 수준 개요"에서 읽기 커밋, 직렬화 가능 및 읽기 전용 격리 수준에 대해 배우기
Oracle 데이터베이스 SQL 언어 참조에서 SQL 표준에 대한 Oracle 데이터베이스의 준수에 대한 논의

Overview of Oracle Database Transaction Isolation Levels

ANSI 표준 트랜잭션 격리 수준은 각 격리 수준에 대해 허용되거나 방지되는 현상을 기준으로 정의됩니다.

Oracle Database는 다음 트랜잭션 격리 수준을 제공합니다:

  • Read Committed Isolation Level (읽기 커밋 격리 수준)
  • Serializable Isolation Level (직렬화 가능 격리 수준)
  • Read-Only Isolation Level (읽기 전용 격리 수준)

참조:

  • Oracle Database Development Guide에서 트랜잭션 격리 수준에 대해 자세히 알아보기
  • Oracle Database SQL Language Reference 및 Oracle Database PL/SQL Language Reference에서 SET TRANSACTION ISOLATION LEVEL에 대해 배우기

Read Committed Isolation Level

읽기 커밋 격리 수준에서는 트랜잭션에 의해 실행된 모든 쿼리가 해당 쿼리가 시작되기 전(트랜잭션이 아님)에 커밋된 데이터만을 봅니다.

이 격리 수준은 기본값입니다. 이는 충돌할 가능성이 적은 트랜잭션이 있는 데이터베이스 환경에 적합합니다.

읽기 커밋 트랜잭션의 쿼리는 쿼리가 진행되는 동안 커밋된 데이터를 읽지 않도록 합니다. 예를 들어, 쿼리가 백만 행 테이블을 반쯤 스캔하는 동안 다른 트랜잭션이 950,000번째 행에 대한 업데이트를 커밋하면 쿼리는 950,000번째 행을 읽을 때 이 변경 사항을 보지 않습니다. 그러나 데이터베이스는 쿼리가 읽은 데이터를 다른 트랜잭션이 수정하지 못하게 하지 않으므로, 다른 트랜잭션은 쿼리 실행 사이에 데이터를 변경할 수 있습니다. 따라서 동일한 쿼리를 두 번 실행하는 트랜잭션은 불명확한 읽기(fuzzy reads)와 유령 읽기(phantoms)를 경험할 수 있습니다.

Read Consistency in the Read Committed Isolation Level

데이터베이스는 사용자의 개입 없이 모든 쿼리에 대해 일관된 결과 집합을 제공하여 데이터 일관성을 보장합니다.

UPDATE 문에서 WHERE 절에 의해 암시된 쿼리와 같은 암시적 쿼리는 일관된 결과 집합을 보장받습니다. 그러나 암시적 쿼리의 각 문은 DML 문 자체에 의해 이루어진 변경 사항을 보지 않고 변경 전의 데이터를 봅니다.

SELECT 목록에 PL/SQL 함수가 포함된 경우 데이터베이스는 PL/SQL 함수 코드 내에서 실행된 SQL에 대해 문 수준 읽기 일관성을 적용하며, 이는 부모 SQL 수준이 아니라 문 수준입니다. 예를 들어, 함수는 다른 사용자가 변경하고 커밋한 테이블의 데이터에 액세스할 수 있습니다. 함수 내 SELECT가 실행될 때마다 새로운 읽기 일관 스냅샷이 설정됩니다.

참조: "서브쿼리"

Conflicting Writes in Read Committed Transactions

읽기 커밋 트랜잭션에서는 트랜잭션이 동시에 실행 중인 커밋되지 않은 트랜잭션에 의해 업데이트된 행을 변경하려고 할 때 충돌 쓰기가 발생합니다.

행 수정을 방지하는 트랜잭션을 차단 트랜잭션이라고도 합니다. 읽기 커밋 트랜잭션은 차단 트랜잭션이 종료되고 행 잠금을 해제할 때까지 기다립니다.

옵션은 다음과 같습니다:

  • 차단 트랜잭션이 롤백되면 대기 중인 트랜잭션은 다른 트랜잭션이 없었던 것처럼 이전에 잠긴 행을 변경할 수 있습니다.
  • 차단 트랜잭션이 커밋되고 잠금을 해제하면 대기 중인 트랜잭션은 새로 변경된 행에 대한 의도된 업데이트를 계속합니다.

다음 표는 직렬화 가능하거나 읽기 커밋일 수 있는 트랜잭션 1과 읽기 커밋 트랜잭션 2 간의 상호 작용을 보여줍니다. 이는 잃어버린 업데이트라고 알려진 고전적인 상황을 보여줍니다. 트랜잭션 1이 수행한 업데이트는 커밋되었음에도 불구하고 테이블에 없습니다. 잃어버린 업데이트를 처리하는 전략을 수립하는 것은 응용 프로그램 개발의 중요한 부분입니다.

표 9-2 읽기 커밋 트랜잭션에서의 충돌 쓰기와 잃어버린 업데이트

세션 1세션 2설명
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda', 'Greene', 'Hintz');LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9500없음
세션 1이 Banda, Greene, Hintz의 급여를 조회합니다. Hintz라는 이름의 직원은 찾을 수 없습니다.
SQL> UPDATE employees SET salary = 7000 WHERE last_name = 'Banda';없음세션 1은 Banda의 급여를 업데이트하여 트랜잭션을 시작합니다. 트랜잭션 1의 기본 격리 수준은 READ COMMITTED입니다.
없음SQL> SET TRANSACTION ISOLATION LEVEL READ COMMITTED;세션 2는 트랜잭션 2를 시작하고 격리 수준을 READ COMMITTED로 명시적으로 설정합니다.
없음SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda', 'Greene', 'Hintz');LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9500
트랜잭션 2는 Banda, Greene, Hintz의 급여를 조회합니다. Oracle Database는 읽기 일관성을 사용하여 트랜잭션 1에 의해 수행된 커밋되지 않은 업데이트 이전의 Banda의 급여를 보여줍니다.
없음SQL> UPDATE employees SET salary = 9900 WHERE last_name = 'Greene';트랜잭션 2는 Greene의 급여를 성공적으로 업데이트합니다. 트랜잭션 1은 Banda 행만 잠갔기 때문입니다.
SQL> INSERT INTO employees (employee_id, last_name, email, hire_date, job_id) VALUES (210, 'Hintz', 'JHINTZ', SYSDATE, 'SH_CLERK');없음트랜잭션 1은 직원 Hintz의 행을 삽입하지만 커밋하지 않습니다.
없음SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda', 'Greene', 'Hintz');LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9900
트랜잭션 2는 Banda, Greene, Hintz의 급여를 조회합니다. 트랜잭션 2는 Greene의 급여에 대한 자신의 업데이트를 봅니다. 트랜잭션 2는 트랜잭션 1에 의해 수행된 Banda의 급여에 대한 커밋되지 않은 업데이트나 Hintz에 대한 삽입을 보지 않습니다.
없음SQL> UPDATE employees SET salary = 6300 WHERE last_name = 'Banda';- 프롬프트가 반환되지 않음
트랜잭션 2는 Banda 행을 업데이트하려고 시도하며, 이는 현재 트랜잭션 1에 의해 잠겨 있어 충돌 쓰기가 발생합니다. 트랜잭션 2는 트랜잭션 1이 종료될 때까지 기다립니다.
SQL> COMMIT;없음트랜잭션 1이 커밋하여 작업을 종료합니다.
없음1 row updated.Banda 행에 대한 잠금이 해제되어 트랜잭션 2는 Banda의 급여에 대한 업데이트를 계속합니다.
없음SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda', 'Greene', 'Hintz');LAST_NAME SALARY ------------- ---------- Banda 6300 Greene 9900 Hintz
트랜잭션 2는 Banda, Greene, Hintz의 급여를 조회합니다. 트랜잭션 1에 의해 커밋된 Hintz 삽입이 이제 트랜잭션 2에 표시됩니다. 트랜잭션 2는 Banda 급여에 대한 자신의 업데이트를 봅니다.
없음COMMIT;트랜잭션 2가 작업을 커밋하여 종료합니다.
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda', 'Greene', 'Hintz');LAST_NAME SALARY ------------- ---------- Banda 6300 Greene 9900 Hintz없음
세션 1은 Banda, Greene, Hintz의 행을 조회합니다. Banda의 급여는 6300으로, 이는 트랜잭션 2에 의해 수행된 업데이트입니다. 트랜잭션 1에 의해 수행

된 Banda의 급여를 7000으로 업데이트한 내용은 이제 "잃어버린" 상태입니다. |

참조:

  • "잠금 사용"에서 잃어버린 업데이트에 대해 배우기
  • "행 잠금(TX)"에서 데이터베이스가 행 잠금을 얻는 시기와 이유에 대해 배우기

Serializable Isolation Level

직렬화 가능 격리 수준에서는 트랜잭션이 트랜잭션이 아닌 쿼리가 시작된 시점과 트랜잭션 자체에 의해 이루어진 변경 사항만을 봅니다.

직렬화 가능한 트랜잭션은 다른 사용자가 데이터베이스의 데이터를 수정하지 않는 것처럼 보이는 환경에서 작동합니다. 직렬화 가능한 격리는 다음과 같은 환경에 적합합니다:

  • 대형 데이터베이스와 소수의 행만 업데이트하는 짧은 트랜잭션이 있는 환경
  • 두 개의 동시 트랜잭션이 동일한 행을 수정할 가능성이 상대적으로 낮은 환경
  • 상대적으로 오래 실행되는 트랜잭션이 주로 읽기 전용인 환경

직렬화 가능 격리 수준에서는 문 수준에서 얻는 읽기 일관성이 전체 트랜잭션에 확장됩니다. 트랜잭션에 의해 읽은 모든 행은 다시 읽을 때 동일하다고 보장됩니다. 모든 쿼리는 트랜잭션 동안 동일한 결과를 반환할 것으로 보장되므로, 다른 트랜잭션에 의해 이루어진 변경 사항은 쿼리가 얼마나 오래 실행되었는지와 관계없이 쿼리에 표시되지 않습니다. 직렬화 가능한 트랜잭션은 더티 읽기, 불명확한 읽기 또는 유령 읽기를 경험하지 않습니다.

Oracle 데이터베이스는 다른 트랜잭션에 의해 변경된 행이 직렬화 가능한 트랜잭션이 시작될 때 이미 커밋된 경우에만 직렬화 가능한 트랜잭션이 행을 수정할 수 있도록 허용합니다. 직렬화 가능한 트랜잭션이 직렬화 가능한 트랜잭션이 시작된 후 다른 트랜잭션에 의해 변경된 데이터를 업데이트하거나 삭제하려고 하면 데이터베이스는 오류를 생성합니다:

ORA-08177: 이 트랜잭션에 대해 액세스를 직렬화할 수 없습니다.

직렬화 가능한 트랜잭션이 ORA-08177 오류로 실패하면 응용 프로그램은 다음과 같은 여러 가지 조치를 취할 수 있습니다:

  • 그 시점까지 실행된 작업을 커밋합니다.
  • 이전에 설정된 저장점을 롤백한 후 추가(하지만 다른) 문을 실행합니다.
  • 전체 트랜잭션을 롤백합니다.

다음 표는 직렬화 가능한 트랜잭션이 다른 트랜잭션과 상호 작용하는 방식을 보여줍니다. 직렬화 가능한 트랜잭션이 직렬화 가능한 트랜잭션이 시작된 후 다른 트랜잭션에 의해 커밋된 행을 변경하려고 하지 않으면 직렬화된 액세스 문제가 방지됩니다.

표 9-3 직렬화 가능한 트랜잭션

세션 1세션 2설명
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda', 'Greene', 'Hintz');LAST_NAME SALARY ------------- ---------- Banda 6200 Greene 9500없음
세션 1은 Banda, Greene, Hintz의 급여를 조회합니다. Hintz라는 이름의 직원은 찾을 수 없습니다.
SQL> UPDATE employees SET salary = 7000 WHERE last_name='Banda';없음세션 1은 Banda의 급여를 업데이트하여 트랜잭션 1을 시작합니다. 기본 격리 수준은 READ COMMITTED입니다.
없음SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;세션 2는 트랜잭션 2를 시작하고 격리 수준을 SERIALIZABLE로 설정합니다.
없음SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda', 'Greene', 'Hintz');LAST_NAME SALARY ------------ ---------- Banda 6200 Greene 9500
트랜잭션 2는 Banda, Greene, Hintz의 급여를 조회합니다. Oracle Database는 트랜잭션 1에 의해 수행된 커밋되지 않은 업데이트 이전의 Banda의 급여를 보여줍니다.
없음SQL> UPDATE employees SET salary = 9900 WHERE last_name = 'Greene';트랜잭션 2는 Greene의 급여를 성공적으로 업데이트합니다. Banda 행만 잠겨 있기 때문입니다.
SQL> INSERT INTO employees (employee_id, last_name, email, hire_date, job_id) VALUES (210, 'Hintz', 'JHINTZ', SYSDATE,'SH_CLERK');없음트랜잭션 1은 직원 Hintz의 행을 삽입합니다.
SQL> COMMIT;없음트랜잭션 1이 작업을 커밋하여 트랜잭션을 종료합니다.
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda', 'Greene', 'Hintz');LAST_NAME SALARY ------------- ---------- Banda 7000 Greene 9500 Hintz없음
세션 1은 Banda, Greene, Hintz의 급여를 조회하여 트랜잭션 1에 의해 커밋된 변경 사항을 봅니다. 세션 1은 트랜잭션 2에 의해 수행된 Greene의 커밋되지 않은 업데이트를 보지 않습니다.
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda', 'Greene', 'Hintz');LAST_NAME SALARY ------------- --------- Banda 6200 Greene 9900
트랜잭션 2는 Banda, Greene, Hintz의 급여를 조회합니다. Oracle Database 읽기 일관성은 트랜잭션 1에 의해 커밋된 Hintz 삽입과 Banda 업데이트가 트랜잭션 2에 표시되지 않도록 합니다. 트랜잭션 2는 Greene의 급여에 대한 자신의 업데이트를 봅니다.
없음COMMIT;트랜잭션 2가 작업을 커밋하여 트랜잭션을 종료합니다.
SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda', 'Greene', 'Hintz');LAST_NAME SALARY ------------- ---------- Banda 7000 Greene 9900 Hintz없음
두 세션은 Banda, Greene, Hintz의 급여를 조회합니다. 각 세션은 트랜잭션 1과 트랜잭션 2에 의해 수행된 모든 커밋된 변경 사항을 봅니다.
SQL> UPDATE employees SET salary = 7100 WHERE last_name = 'Hintz';없음세션 1은 Hintz의 급여를 업데이트하여 트랜잭션 3을 시작합니다. 트랜잭션 3의 기본 격리 수준은 READ COMMITTED입니다.
없음SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;세션 2는 트랜잭션 4를 시작하고 격리 수준을 SERIALIZABLE로 설정합니다.
없음SQL> UPDATE employees SET salary = 7200 WHERE last_name = 'Hintz';- 프롬프트가 반환되지 않음
트랜잭션 4는 Hintz의 급여를 업데이트하려고 시도하지만 트랜잭션 3이 Hintz 행을 잠갔기 때문에 차단됩니다. 트랜잭션 4는 트랜잭션 3 뒤에 대기합니다.
SQL> COMMIT;없음트랜잭션 3이 Hintz 급여 업데이트를 커밋하여 트랜잭션을 종료합니다.
없음UPDATE employees SET salary = 7200 WHERE last_name = 'Hintz' * ERROR at line 1: ORA-08177: can't serialize access for this transaction트랜잭션 3을 종료하는 커밋은 트랜잭션 4에서 ORA-08177 오류와 함께 Hintz 업데이트를 실패하게 합니다. 오류는 트랜잭션 3이 트랜잭션 4가 시작된 후 Hintz 업데이트를 커밋했기 때문에 발생합니다.
없음SQL> ROLLBACK;세션 2는 트랜잭션 4를 롤백하여 트랜잭션을 종료합니다.
없음SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;세션 2는 트랜잭션 5를 시작하고 격리 수준을 SERIALIZABLE로 설정합니다.
없음SQL> SELECT last_name, salary FROM employees WHERE last_name IN ('Banda', 'Greene', 'Hintz');LAST_NAME SALARY ------------- ---------- Banda 7000 Greene 9900 Hintz 7100

트랜잭션 5는 Banda, Greene, Hintz의 급여를 조회합니다. 트랜잭션 3에 의해 커밋된 Hintz 급여 업데이트가 표시됩니다. |
| 없음 | SQL> UPDATE employees SET salary = 7200 WHERE last_name = 'Hintz'; | 1 row updated. |
| | | 트랜잭션 5는 Hintz 급여를 다른 값으로 업데이트합니다. 트랜잭션 3에 의해 수행된 Hintz 업데이트가 트랜잭션 5가 시작되기 전에 커밋되었기 때문에 직렬화된 액세스 문제가 방지됩니다. |
| | | 참고: 다른 트랜잭션이 트랜잭션 5가 시작된 후 Hintz 행을 업데이트하고 커밋한 경우, 직렬화된 액세스 문제가 다시 발생합니다. |
| 없음 | SQL> COMMIT; | 세션 2는 문제 없이 업데이트를 커밋하여 트랜잭션을 종료합니다. |

참조:

  • "행 잠금(TX)"
  • "트랜잭션 제어 개요"

Read-Only Isolation Level

읽기 전용 격리 수준은 직렬화 가능 격리 수준과 유사하지만, SYS 사용자가 아닌 경우 트랜잭션 내에서 데이터를 수정할 수 없습니다.

읽기 전용 트랜잭션은 ORA-08177 오류에 취약하지 않습니다. 읽기 전용 트랜잭션은 트랜잭션이 시작된 시점에 대해 일관된 내용을 갖는 보고서를 생성하는 데 유용합니다.

Oracle 데이터베이스는 undo 세그먼트에서 필요한 경우 데이터를 재구성하여 읽기 일관성을 달성합니다. undo 세그먼트는 순환 방식으로 사용되므로 데이터베이스는 undo 데이터를 덮어쓸 수 있습니다. 장기 실행 보고서는 읽기 일관성을 위해 필요한 undo 데이터가 다른 트랜잭션에 의해 재사용되었을 위험이 있으며, 이는 스냅샷이 너무 오래되었음을 나타내는 오류를 발생시킬 수 있습니다. 데이터베이스가 오래된 undo 데이터를 덮어쓰지 않도록 최소 시간을 유지하려고 시도하는 undo 보유 기간을 설정하여 이 문제를 피할 수 있습니다.

참조:

  • "Undo 세그먼트"
  • Oracle Database Administrator’s Guide에서 undo 보유 기간 설정 방법 배우기

Overview of the Oracle Database Locking Mechanism

잠금(lock)은 파괴적인 상호작용을 방지하는 메커니즘입니다.

상호작용이 파괴적일 때는 트랜잭션이 공유 데이터를 접근하는 과정에서 데이터를 잘못 업데이트하거나 기본 데이터 구조를 잘못 변경할 때입니다. 잠금은 데이터베이스 동시성과 일관성을 유지하는 데 중요한 역할을 합니다.

Summary of Locking Behavior

데이터베이스는 잠금을 획득한 작업에 따라 여러 종류의 잠금을 유지합니다.

일반적으로 데이터베이스는 두 가지 유형의 잠금을 사용합니다: 배타 잠금과 공유 잠금. 하나의 배타 잠금은 행 또는 테이블과 같은 리소스에서만 획득할 수 있지만, 여러 공유 잠금은 단일 리소스에서 획득할 수 있습니다.

잠금은 읽기와 쓰기 작업 간의 상호작용에 영향을 미칩니다. 리더는 리소스를 조회하는 쿼리이고, 라이터는 리소스를 수정하는 문장입니다. 다음 규칙은 Oracle Database의 읽기와 쓰기 작업에 대한 잠금 동작을 요약합니다:

  • 행은 라이터에 의해 수정될 때만 잠깁니다.
  • 문장이 하나의 행을 업데이트할 때, 트랜잭션은 이 행에 대해서만 잠금을 획득합니다. 행 수준에서 테이블 데이터를 잠금으로써 데이터베이스는 동일한 데이터에 대한 경쟁을 최소화합니다. 정상적인 상황에서는 데이터베이스가 행 잠금을 블록 또는 테이블 수준으로 확장하지 않습니다.
  • 하나의 행을 작성하는 라이터는 동일한 행을 동시에 작성하는 다른 라이터를 차단합니다.
  • 한 트랜잭션이 행을 수정하고 있다면, 행 잠금은 다른 트랜잭션이 동시에 동일한 행을 수정하지 못하게 합니다.
  • 리더는 라이터를 차단하지 않습니다.
  • 행을 읽는 리더는 행을 잠그지 않기 때문에 라이터가 이 행을 수정할 수 있습니다. 유일한 예외는 SELECT ... FOR UPDATE 문으로, 이는 읽고 있는 행을 잠그는 특수한 유형의 SELECT 문입니다.
  • 라이터는 리더를 차단하지 않습니다.
  • 라이터가 행을 변경할 때, 데이터베이스는 읽기 작업자에게 행의 일관된 보기를 제공하기 위해 undo 데이터를 사용합니다.

참고: 데이터 리더는 분산 트랜잭션이 보류 중인 매우 특수한 경우에 데이터 작성자를 기다려야 할 수 있습니다.

참조:

  • SELECT ... FOR UPDATE에 대해 배우려면 Oracle Database SQL 언어 참조를 참조하십시오.
  • 의심스러운 분산 트랜잭션과 관련된 대기에 대해 배우려면 Oracle Database Administrator’s Guide를 참조하십시오.

Use of Locks

단일 사용자 데이터베이스에서는 잠금이 필요하지 않습니다. 한 명의 사용자만 정보를 수정하기 때문입니다. 그러나 여러 사용자가 데이터를 접근하고 수정할 때, 데이터베이스는 동일한 데이터를 동시에 수정하는 것을 방지하는 방법을 제공해야 합니다.

잠금은 다음과 같은 중요한 데이터베이스 요구 사항을 달성합니다:

  • 일관성: 세션이 보고 있거나 변경하고 있는 데이터는 사용자가 작업을 끝낼 때까지 다른 세션에 의해 변경되지 않아야 합니다.
  • 무결성: 데이터와 구조는 올바른 순서로 이루어진 모든 변경 사항을 반영해야 합니다.

Oracle Database는 잠금 메커니즘을 통해 트랜잭션 간 데이터 동시성, 일관성, 무결성을 제공합니다. 잠금은 자동으로 발생하며 사용자 조치가 필요하지 않습니다.

잠금의 필요성을 단일 행의 동시 업데이트로 설명할 수 있습니다. 다음 예제에서는 간단한 웹 기반 응용 프로그램이 최종 사용자에게 직원 이메일과 전화번호를 제시하는 방법을 보여줍니다. 응용 프로그램은 다음과 같은 UPDATE 문을 사용하여 데이터를 수정합니다:

UPDATE employees
SET email = ?, phone_number = ?
WHERE employee_id = ?
AND email = ?
AND phone_number = ?

위의 UPDATE 문에서, WHERE 절에 있는 이메일과 전화번호 값은 지정된 직원에 대한 원래의 수정되지 않은 값입니다. 이 업데이트는 응용 프로그램이 수정하는 행이 응용 프로그램이 마지막으로 읽고 사용자에게 표시한 이후 변경되지 않았음을 보장합니다. 이렇게 함으로써 응용 프로그램은 한 사용자가 다른 사용자가 수행한 변경 사항을 덮어써서 두 번째 사용자의 업데이트가 효과적으로 "잃어버린" 업데이트 문제를 피합니다. (표 9-2는 잃어버린 업데이트의 예를 보여줍니다).

표 9-4 행 잠금 예제

T세션 1세션 2설명
t0SELECT employee_id as ID, email, phone_number FROM hr.employees WHERE last_name='Himuro';ID EMAIL PHONE_NUMBER --- ------- ------------ 118 GHIMURO 515.127.4565세션 1에서 hr1 사용자가 hr.employees에서 Himuro 레코드를 조회하여 employee_id (118), email (GHIMURO), 전화번호 (515.127.4565)를 표시합니다.
t1SELECT employee_id as ID, email, phone_number FROM hr.employees WHERE last_name='Himuro'; ID EMAIL PHONE_NUMBER --- ------- ------------ 118 GHIMURO 515.127.4565세션 2에서 hr2 사용자가 hr.employees에서 Himuro 레코드를 조회하여 employee_id (118), email (GHIMURO), 전화번호 (515.127.4565)를 표시합니다.
t2UPDATE hr.employees SET phone_number='515.555.1234' WHERE employee_id=118 AND email='GHIMURO' AND phone_number = '515.127.4565';1 row updated.세션 1에서 hr1 사용자가 행의 전화번호를 515.555.1234로 업데이트하여 GHIMURO 행에 잠금을 획득합니다.
t3UPDATE hr.employees SET phone_number='515.555.1235' WHERE employee_id=118 AND email='GHIMURO' AND phone_number = '515.127.4565';세션 2에서 hr2 사용자가 동일한 행을 업데이트하려고 시도하지만 hr1이 현재 행을 처리 중이므로 차단됩니다. hr2의 시도된 업데이트는 hr1 업데이트와 거의 동시에 발생합니다.
t4COMMIT;Commit complete.세션 1에서 hr1 사용자가 트랜잭션을 커밋합니다. 커밋은 Himuro의 변경을 영구적으로 만들고 세션 2의 차단을 해제합니다.
t50 rows updated.세션 2에서 hr2 사용자는 GHIMURO 행이 더 이상 조건과 일치하지 않도록 수정되었음을 발견합니다. 조건이 일치하지 않기 때문에 세션 2는 레코드를 업데이트하지 않습니다.
t6UPDATE hr.employees SET phone_number='515.555.1235' WHERE employee_id=118 AND email='GHIMURO' AND phone_number='515.555.1234';1 row updated.세션 1에서 hr1 사용자가 GHIMURO 행을 잘못된 전화번호로 업데이트했음을 인식하고, 새로운 트랜잭션을 시작하여 행의 전화번호를 515.555.1235로 업데이트하여 GHIMURO 행에 잠금을 겁니다.
t7SELECT employee_id as ID, email, phone_number FROM hr.employees WHERE last_name='Himuro'; ID EMAIL PHONE_NUMBER --- ------- ------------ 118 GHIMURO 515.555.1234세션 2에서 hr2 사용자가 hr.employees에서 Himuro 레코드를 조회합니다. 레코드는 t4에서 세션 1이 커밋한 전화번호 업데이트를 보여줍니다. Oracle Database의 읽기 일관성은 세션 2가 t6에서 이루어진 커밋되지 않은 변경 사항을 보지 않도록 합니다.
t8UPDATE hr.employees SET phone_number='515.555.1235 WHERE employee_id=118 AND email='GHIMURO' AND phone_number='515.555.1234';세션 2에서 hr2 사용자가 동일한 행을 업데이트하려고 시도하지만 hr1이 현재 행을 처리 중이므로 차단됩니다.
t9ROLLBACK;Rollback complete.세션 1에서 hr1 사용자가 트랜잭션을 롤백하여 종료합니다.
t101 row updated.세션 2에서 전화번호 업데이트가 성공합니다. 세션 1의 업데이트가 롤백되었기 때문입니다. GHIMURO 행은 조건과 일치하므로 업데이트가 성공합니다.
t11COMMIT; Commit complete.세션 2가 업데이트

를 커밋하여 트랜잭션을 종료합니다. |

Oracle Database는 SQL 문을 실행할 때 필요한 잠금을 자동으로 획득합니다. 예를 들어, 데이터베이스가 세션이 데이터를 수정할 수 있도록 허용하기 전에 세션은 먼저 데이터를 잠가야 합니다. 잠금은 세션이 데이터를 독점적으로 제어할 수 있게 하여 잠금이 해제될 때까지 다른 트랜잭션이 잠긴 데이터를 수정할 수 없도록 합니다.

Oracle Database의 잠금 메커니즘은 트랜잭션 제어와 밀접하게 연관되어 있으므로 응용 프로그램 설계자는 트랜잭션을 올바르게 정의하기만 하면 Oracle Database가 자동으로 잠금을 관리합니다. 사용자는 어떤 리소스도 명시적으로 잠글 필요가 없지만, Oracle Database는 사용자가 데이터를 수동으로 잠글 수 있도록 합니다.

다음 섹션에서는 Oracle Database가 데이터 동시성을 달성하는 방법을 이해하는 데 중요한 개념을 설명합니다.

참조:

  • OWA_OPT_LOCK 패키지에 대해 배우려면 Oracle Database PL/SQL Packages and Types Reference를 참조하십시오. 이 패키지에는 잃어버린 업데이트를 방지하는 데 도움이 되는 서브프로그램이 포함되어 있습니다.

Lock Modes

Oracle Database는 데이터 동시성을 최대한 높이고 안전한 데이터 무결성을 제공하기 위해 적용 가능한 가장 낮은 수준의 제한을 자동으로 사용합니다.

제한 수준이 낮을수록 다른 사용자가 데이터를 접근할 수 있는 가능성이 높아집니다. 반대로, 제한 수준이 높을수록 다른 트랜잭션이 획득할 수 있는 잠금 유형이 더 제한됩니다.

Oracle Database는 다중 사용자 데이터베이스에서 두 가지 잠금 모드를 사용합니다:

  • 배타 잠금 모드: 이 모드는 관련 리소스가 공유되지 않도록 합니다. 트랜잭션이 데이터를 수정할 때 배타 잠금을 획득합니다. 리소스를 독점적으로 잠그는 첫 번째 트랜잭션은 배타 잠금이 해제될 때까지 리소스를 수정할 수 있는 유일한 트랜잭션입니다.
  • 공유 잠금 모드: 이 모드는 관련 리소스가 관련 작업에 따라 공유될 수 있도록 합니다. 여러 사용자가 데이터를 읽는 경우, 각 사용자가 배타 잠금을 필요로 하는 라이터의 동시 접근을 방지하기 위해 공유 잠금을 유지합니다. 여러 트랜잭션이 동일한 리소스에 대해 공유 잠금을 획득할 수 있습니다.

트랜잭션이 SELECT ... FOR UPDATE 문을 사용하여 단일 테이블 행을 선택한다고 가정해 봅시다. 트랜잭션은 배타 행 잠금과 행 공유 테이블 잠금을 획득합니다. 행 잠금은 다른 세션이 잠긴 행 이외의 행을 수정할 수 있도록 하고, 테이블 잠금은 세션이 테이블의 구조를 변경하지 못하도록 합니다. 따라서 데이터베이스는 가능한 많은 문장이 실행되도록 허용합니다.

Lock Conversion and Escalation

Oracle Database는 필요한 경우 잠금 변환을 수행합니다.

잠금 변환에서는 데이터베이스가 낮은 제한의 테이블 잠금을 더 높은 제한의 잠금으로 자동 변환합니다. 예를 들어, 트랜잭션이 직원에 대해 SELECT ... FOR UPDATE를 실행하고 나중에 잠긴 행을 업데이트한다고 가정합니다. 이 경우, 데이터베이스는 자동으로 행 공유 테이블 잠금을 행 배타 테이블 잠금으로 변환합니다. 트랜잭션은 트랜잭션 내에서 삽입, 업데이트 또는 삭제된 모든 행에 대해 배타 행 잠금을 유지합니다. 행 잠금은 가장 높은 제한 수준으로 획득되므로 잠금 변환이 필요하거나 수행되지 않습니다.

잠금 변환은 많은 잠금이 한 수준의 세분성(예: 행)에서 유지되고 데이터베이스가 잠금을 더 높은 세분성 수준(예: 테이블)으로 상승시키는 잠금 확장과 다릅니다. 세션이 테이블에서 많은 행을 잠그면 일부 데이터베이스는 자동으로 행 잠금을 단일 테이블로 확장합니다. 잠금 수는 줄어들지만 잠금 제한성이 증가합니다.

Oracle Database는 절대 잠금을 확장하지 않습니다. 잠금 확장은 교착 상태 발생 확률을 크게 증가시킵니다. 시스템이 트랜잭션 1을 위해 잠금을 확장하려고 하지만 트랜잭션 2가 유지하는 잠금 때문에 확장할 수 없는 경우를 가정해 보겠습니다. 트랜잭션 2가 진행하기 위해 동일한 데이터의 잠금 확장을 필요로 하면 교착 상태가 발생합니다.

Lock Duration

Oracle Database는 트랜잭션이 더 이상 리소스를 필요로 하지 않게 되는 이벤트가 발생하면 자동으로 잠금을 해제합니다.

일반적으로 데이터베이스는 트랜잭션 내에서 문장이 획득한 잠금을 트랜잭션이 지속되는 동안 유지합니다. 이러한 잠금은 동시 트랜잭션의 더티 읽기, 잃어버린 업데이트 및 파괴적인 DDL과 같은 파괴적 간섭을 방지합니다.

참고: 인덱스되지 않은 외래 키 때문에 취해진 자식 테이블의 테이블 잠금은 트랜잭션이 아닌 문장 동안 유지됩니다. 또한 DBMS_LOCK 패키지는 사용자가 정의한 잠금을 원하는 대로 해제하고 할당할 수 있으며 트랜잭션 경계를 넘어서도 유지할 수 있습니다.

Oracle Database는 트랜잭션이 커밋되거나 롤백될 때 해당 트랜잭션 내의 문장이 획득한 모든 잠금을 해제합니다. 또한, 데이터베이스는 저장점 이후에 획득된 잠금을 저장점으로 롤백할 때 해제합니다. 그러나 이전에 잠긴 리소스를 기다리지 않는 트랜잭션만이 이제 사용 가능한 리소스에 대한 잠금을 획득할 수 있습니다. 대기 중인 트랜잭션은 원래 트랜잭션이 완전히 커밋되거나 롤백될 때까지 계속 기다립니다.

참조:

  • "표 10-3"에서 트랜잭션 대기 동작을 보여줍니다.
  • DBMS_LOCK에 대해 배우려면 "사용자 정의 잠금 개요"를 참조하십시오.

Locks and Deadlocks

교착 상태는 두 명 이상의 사용자가 서로의 잠금을 기다리는 상황입니다. 교착 상태는 일부 트랜잭션이 계속 작업을 수행하지 못하게 합니다.

Oracle Database는 자동으로 교착 상태를 감지하고, 교착 상태에 관련된 하나의 문장을 롤백하여 해결합니다. 데이터베이스는 교착 상태를 감지한 트랜잭션에 해당하는 메시지를 반환하여 해당 문장이 롤백됩니다. 일반적으로 신호가 전달된 트랜잭션은 명시적으로 롤백되어야 하지만, 대기 후 롤백된 문장을 다시 시도할 수 있습니다.

표 9-5 교착 상태 트랜잭션

T세션 1세션 2설명
t0SQL> UPDATE employees SET salary = salary*1.1 WHERE employee_id = 100;1 row updated.세션 1이 트랜잭션 1을 시작하여 직원 100의 급여를 업데이트합니다. 세션 2가 트랜잭션 2를 시작하여 직원 200의 급여를 업데이트합니다. 각 트랜잭션이 시도하는 행만 잠그므로 문제가 없습니다.
t1SQL> UPDATE employees SET salary = salary*1.1 WHERE employee_id = 200;-- 프롬프트가 반환되지 않음트랜잭션 1이 직원 200 행을 업데이트하려고 시도하지만, 이는 현재 트랜잭션 2에 의해 잠겨 있습니다. 트랜잭션 2가 직원 100 행을 업데이트하려고 시도하지만, 이는 현재 트랜잭션 1에 의해 잠겨 있습니다. 교착 상태가 발생합니다.
t2UPDATE employees * ERROR at line 1: ORA-00060: deadlock detected while waiting for resource트랜잭션 1이 교착 상태를 신호하고 t1에 발행된 UPDATE 문을 롤백합니다. 그러나 t0에서 수행된 업데이트는 롤백되지 않습니다.
t3SQL> COMMIT; Commit complete.세션 1이 t0에서 수행된 업데이트를 커밋하여 트랜잭션 1을 종료합니다. t1에서 시도된 업데이트는 커밋되지 않습니다.
t41 row updated.트랜잭션 1에 의해 차단된 트랜잭션 2의 t1 업데이트가 실행됩니다.
t5SQL> COMMIT; Commit complete.세션 2가 t0 및 t1에서 수행된 업데이트를 커밋하여 트랜잭

션 2를 종료합니다. |

교착 상태는 주로 트랜잭션이 Oracle Database의 기본 잠금을 명시적으로 재정의할 때 발생합니다. Oracle Database는 잠금을 확장하지 않고 쿼리에 대해 읽기 잠금을 사용하지 않으며, 페이지 수준 대신 행 수준 잠금을 사용하므로 교착 상태는 드뭅니다.

참조:

  • "수동 데이터 잠금 개요"
  • 테이블을 명시적으로 잠글 때 교착 상태를 처리하는 방법을 배우려면 Oracle Database Development Guide를 참조하십시오.

Overview of Automatic Locks

Oracle Database는 다른 트랜잭션이 동일한 리소스에 대해 독점적인 접근이 필요한 작업을 수행하지 못하도록 트랜잭션을 대신하여 리소스를 자동으로 잠급니다.

데이터베이스는 리소스와 수행 중인 작업에 따라 서로 다른 제한 수준의 다양한 유형의 잠금을 자동으로 획득합니다.

참고: 데이터베이스는 단순 읽기 작업을 수행할 때 행을 잠그지 않습니다.

Oracle Database의 잠금은 다음 표에 나와 있는 범주로 나뉩니다.

표 9-6 잠금 범주

잠금설명더 알아보기
DML 잠금데이터를 보호합니다. 예를 들어, 테이블 잠금은 전체 테이블을 잠그고, 행 잠금은 선택된 행을 잠급니다."DML 잠금"
DDL 잠금스키마 객체의 구조를 보호합니다. 예를 들어, 테이블과 뷰의 사전 정의를 보호합니다."DDL 잠금"
시스템 잠금데이터 파일과 같은 내부 데이터베이스 구조를 보호합니다. 래치, 뮤텍스 및 내부 잠금은 완전히 자동입니다."시스템 잠금"

DML Locks

DML 잠금, 또는 데이터 잠금은 여러 사용자가 동시에 접근하는 데이터의 무결성을 보장합니다.

예를 들어, DML 잠금은 두 고객이 온라인 서점에서 마지막 남은 책을 동시에 구매하지 못하도록 합니다. DML 잠금은 동시 충돌 DML 또는 DDL 작업의 파괴적 간섭을 방지합니다.

DML 문장은 다음 유형의 잠금을 자동으로 획득합니다:

  • 행 잠금 (TX)
  • 테이블 잠금 (TM)

다음 섹션에서는 각 잠금 또는 잠금 모드의 약어가 Oracle Enterprise Manager의 잠금 모니터에서 사용되는 약어입니다. Enterprise Manager는 모든 테이블 잠금에 대해 TM을 표시할 수 있으며, 테이블 잠금 모드(RS 또는 SRX)를 나타내지 않을 수 있습니다.

참조: "Oracle Enterprise Manager"

Row Locks (TX)

행 잠금, 또는 TX 잠금은 테이블의 단일 행에 대한 잠금입니다. 트랜잭션은 INSERT, UPDATE, DELETE, MERGE 또는 SELECT ... FOR UPDATE 문에 의해 수정된 각 행에 대해 행 잠금을 획득합니다. 행 잠금은 트랜잭션이 커밋되거나 롤백될 때까지 존재합니다.

행 잠금은 주로 두 트랜잭션이 동일한 행을 수정하지 못하도록 하는 대기 메커니즘으로 작동합니다. 데이터베이스는 항상 수정된 행을 배타 모드로 잠그므로 다른 트랜잭션이 잠금을 가진 트랜잭션이 커밋되거나 롤백될 때까지 행을 수정할 수 없습니다. 행 잠금은 가능한 가장 세분화된 잠금을 제공하므로 최고의 동시성과 처리량을 제공합니다.

참고: 데이터베이스 인스턴스 실패로 인해 트랜잭션이 종료되면 블록 수준 복구가 전체 트랜잭션이 복구되기 전에 행을 사용할 수 있게 합니다.
트랜잭션이 행에 대한 잠금을 획득하면 해당 행이 포함된 테이블에 대한 잠금도 획득합니다. 테이블 잠금은 현재 트랜잭션에서 데이터 변경을 덮어쓸 수 있는 충돌하는 DDL 작업을 방지합니다. 그림 9-2는 테이블의 세 번째 행을 업데이트하는 예를 보여줍니다. Oracle Database는 자동으로 업데이트된 행에 배타 잠금을 설정하고 테이블에 부분 배타 잠금을 설정합니다.

그림 9-2 행 및 테이블 잠금

그림 9-2 설명
그림 9-2 "행 및 테이블 잠금" 설명

Row Locks and Concurrency

이 시나리오는 Oracle Database가 동시성을 위해 행 잠금을 사용하는 방법을 보여줍니다.

세 개의 세션이 동시에 동일한 행을 조회합니다. 세션 1과 2는 서로 다른 행에 대해 커밋되지 않은 업데이트를 수행하고, 세션 3은 업데이트를 수행하지 않습니다. 각 세션은 자신이 수행한 커밋되지 않은 업데이트만 볼 수 있으며 다른 세션이 수행한 커밋되지 않은 업데이트는 볼 수 없습니다.

표 9-7 데이터 동시성 예제

T세션 1세션 2세션 3설명
t0SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101); EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101); EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101); EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600세 개의 서로 다른 세션이 동시에 직원 100과 101의 ID와 급여를 조회합니다. 각 쿼리에 의해 반환된 결과는 동일합니다.
t1UPDATE hr.employees SET salary = salary+100 WHERE employee_id=100;세션 1은 직원 100의 급여를 업데이트하지만 커밋하지 않습니다. 업데이트에서 작성자는 업데이트된 행에 대해서만 행 수준 잠금을 획득하여 다른 작성자가 이 행을 수정하지 못하도록 합니다.
t2SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101); EMPLOYEE_ID SALARY ----------- ------ 100 612 101 600SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101); EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101); EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600각 세션은 원래 쿼리를 동시에 실행합니다. 세션 1은 t1 업데이트 결과로 급여 612를 보여줍니다. 세션 2와 3의 리더는 트랜잭션이 끝날 때까지 기다리지 않고 즉시 행을 반환합니다. 데이터베이스는 다중 버전 읽기 일관성을 사용하여 세션 1의 업데이트 이전의 급여를 보여줍니다.
t3UPDATE hr.employee SET salary = salary+100 WHERE employee_id=101;세션 2는 직원 101의 급여를 업데이트하지만 트랜잭션을 커밋하지 않습니다. 업데이트에서 작성자는 업데이트된 행에 대해서만 행 수준 잠금을 획득하여 다른 작성자가 이 행을 수정하지 못하도록 합니다.
t4SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101); EMPLOYEE_ID SALARY ----------- ------ 100 612 101 600SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101); EMPLOYEE_ID SALARY ----------- ------ 100 512 101 700SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101); EMPLOYEE_ID SALARY ----------- ------ 100 512 101 600각 세션은 원래 쿼리를 동시에 실행합니다. 세션 1은 t1 업데이트 결과로 급여 612를 보여주지만, 세션 2에서 직원 101에 대해 수행된 급여 업데이트는 보여주지 않습니다. 세션 2의 리더는 세션 2에서 수행된 급여 업데이트를 보여주지만, 세션 1에서 수행된 급여 업데이트는 보여주지 않습니다. 세션 3의 리더는 세션 1과 2에서 수정하기 전의 급여를 읽기 일관성을 사용하여 보여줍니다.

참조:

  • Oracle Database SQL Language Reference
  • V$LOCK에 대해 배우려면 Oracle Database Reference를 참조하십시오.

Storage of Row Locks

일부 데이터베이스는 메모리에 있는 잠금 목록을 유지하기 위해 잠금 관리자를 사용하는 것과 달리, Oracle Database는 잠긴 행이 포함된 데이터 블록에 잠금 정보를 저장합니다.

데이터베이스는 행 잠금 획득을 위한 대기 메커니즘을 사용합니다. 트랜잭션이 잠금되지 않은 행에 대한 잠금을 필요로 하면 데이터 블록에 잠금을 배치합니다. 이 트랜잭션에 의해 수정된 각 행은 블록 헤더에 저장된 트랜잭션 ID 복사본을 가리킵니다.

트랜잭션이 종료되면 트랜잭션 ID는 블록 헤더에 남아 있습니다. 다른 트랜잭션이 행을 수정하려고 하면 트랜잭션 ID를 사용하여 잠금이 활성 상태인지 여부를 결정합니다. 잠금이 활성 상태이면 세션은 잠금이 해

제될 때 알림을 요청합니다. 그렇지 않으면 트랜잭션이 잠금을 획득합니다.

참조: "데이터 블록 개요"에서 데이터 블록 헤더에 대해 더 알아보기
V$TRANSACTION에 대해 배우려면 Oracle Database Reference를 참조하십시오.

Table Locks (TM)

테이블 잠금, 또는 TM 잠금은 트랜잭션이 INSERT, UPDATE, DELETE, MERGE, SELECT with the FOR UPDATE 절 또는 LOCK TABLE 문으로 테이블을 수정할 때 획득됩니다.

DML 작업은 트랜잭션을 대신하여 테이블에 대한 DML 접근을 예약하고 트랜잭션과 충돌할 수 있는 DDL 작업을 방지하기 위해 테이블 잠금을 필요로 합니다.

테이블 잠금은 다음 모드 중 하나로 유지될 수 있습니다:

  • 행 공유 (RS): 행 공유 잠금, 또는 부분 공유 테이블 잠금(SS)은 잠금을 보유한 트랜잭션이 테이블에서 행을 잠그고 업데이트할 의도가 있음을 나타냅니다. 행 공유 잠금은 테이블 잠금의 가장 낮은 제한 모드로, 테이블의 동시성을 가장 높게 제공합니다.
  • 행 배타 테이블 잠금 (RX): 행 배타 테이블 잠금, 또는 부분 배타 테이블 잠금(SX)은 일반적으로 잠금을 보유한 트랜잭션이 테이블 행을 업데이트했거나 SELECT ... FOR UPDATE를 실행했음을 나타냅니다. SX 잠금은 다른 트랜잭션이 동일한 테이블에서 행을 동시에 쿼리, 삽입, 업데이트, 삭제 또는 잠글 수 있도록 합니다. 따라서 SX 잠금은 동일한 테이블에 대해 여러 트랜잭션이 동시에 SX 및 부분 공유 테이블 잠금을 획득할 수 있도록 합니다.
  • 공유 테이블 잠금 (S): 트랜잭션이 보유한 공유 테이블 잠금은 다른 트랜잭션이 테이블을 쿼리할 수 있도록 허용하지만(SELECT ... FOR UPDATE 사용 제외), 단일 트랜잭션이 공유 테이블 잠금을 보유한 경우에만 업데이트가 허용됩니다. 여러 트랜잭션이 동시에 공유 테이블 잠금을 보유할 수 있으므로, 이 잠금을 보유하는 것만으로는 트랜잭션이 테이블을 수정할 수 있음을 보장할 수 없습니다.
  • 공유 행 배타 테이블 잠금 (SRX): 공유 행 배타 테이블 잠금, 또는 공유 부분 배타 테이블 잠금(SSX)은 공유 테이블 잠금보다 더 제한적입니다. 특정 테이블에 대해 한 번에 하나의 트랜잭션만 SSX 잠금을 획득할 수 있습니다. SSX 잠금을 보유한 트랜잭션은 다른 트랜잭션이 테이블을 쿼리할 수 있도록 허용하지만(SELECT ... FOR UPDATE 제외), 테이블을 업데이트할 수는 없습니다.
  • 배타 테이블 잠금 (X): 이 잠금은 가장 제한적이며, 다른 트랜잭션이 테이블에 대해 어떤 유형의 DML 문을 수행하거나 잠금을 설정하는 것을 금지합니다.

참조: Oracle Database SQL Language Reference
테이블 잠금에 대해 더 알아보려면 Oracle Database Development Guide를 참조하십시오.

Locks and Foreign Keys

Oracle Database는 종속 외래 키와 관련된 부모 키의 동시성 제어를 최대화합니다.

잠금 동작은 외래 키 열이 인덱스되었는지 여부에 따라 다릅니다. 외래 키가 인덱스되지 않은 경우, 자식 테이블은 더 자주 잠길 가능성이 있으며 교착 상태가 발생하고 동시성이 감소할 것입니다. 이러한 이유로 외래 키는 거의 항상 인덱스되어야 합니다. 유일한 예외는 일치하는 고유 키나 기본 키가 절대 업데이트되거나 삭제되지 않는 경우입니다.

Locks and Unindexed Foreign Keys

자식 테이블의 외래 키 열에 인덱스가 없는 경우, 세션이 부모 테이블의 기본 키를 수정하거나(예: 행을 삭제하거나 기본 키 속성을 수정) 부모 테이블에 행을 병합할 때 데이터베이스는 자식 테이블에 대한 전체 테이블 잠금을 획득합니다.

다음 두 가지 조건이 참인 경우, 데이터베이스는 자식 테이블에 대한 전체 테이블 잠금을 획득합니다:

  • 자식 테이블의 외래 키 열에 인덱스가 없습니다.
  • 세션이 부모 테이블의 기본 키를 수정합니다(예: 행을 삭제하거나 기본 키 속성을 수정) 또는 부모 테이블에 행을 병합합니다.

참고: 부모 테이블에 삽입하는 경우 자식 테이블에서 DML을 방지하는 차단 테이블 잠금을 획득하지 않습니다. 삽입의 경우, 데이터베이스는 자식 테이블의 구조 변경을 방지하는 잠금을 획득하지만 기존 또는 새로 추가된 행을 수정하는 것을 방지하지 않습니다.

예를 들어, hr.departments 테이블이 hr.employees의 부모이며, hr.employees에는 인덱스되지 않은 외래 키 employees.department_id가 포함되어 있다고 가정해 봅시다. 다음 그림은 세션이 departments 테이블에서 부서 60의 기본 키 속성을 수정하는 상황을 보여줍니다.

그림 9-3 인덱스되지 않은 외래 키와 잠금 메커니즘

그림 9-3 설명
그림 9-3 "인덱스되지 않은 외래 키와 잠금 메커니즘" 설명

그림 9-3에서 데이터베이스는 부서 60의 기본 키 수정 중에 employees에 대한 전체 테이블 잠금을 획득합니다. 이 잠금은 다른 세션이 employees 테이블을 업데이트하지 못하게 하지만 조회는 가능하게 합니다. 예를 들어, 세션은 직원 전화번호를 업데이트할 수 없습니다. employees에 대한 테이블 잠금은 departments 테이블에서 기본 키 수정이 완료된 직후 해제됩니다. departments에서 여러 행이 기본 키 수정이 이루어지면 employees에 대한 테이블 잠금은 수정된 각 행에 대해 한 번씩 획득되고 해제됩니다.

참고: 자식 테이블에서 DML은 부모 테이블에 대한 테이블 잠금을 획득하지 않습니다.

Locks and Indexed Foreign Keys

자식 테이블의 외래 키 열이 인덱스된 경우, 세션이 부모 테이블의 기본 키를 수정하거나(예: 행을 삭제하거나 기본 키 속성을 수정) 부모 테이블에 행을 병합할 때 데이터베이스는 자식 테이블에 대한 전체 테이블 잠금을 획득하지 않습니다.

부모 테이블에 대한 잠금은 트랜잭션이 배타 테이블 잠금을 획득하지 못하게 하지만, 기본 키 수정 동안 부모 또는 자식 테이블에 대한 DML을 방지하지 않습니다. 부모 테이블에서 기본 키 수정이 발생하는 동안 자식 테이블에서 업데이트가 발생하는 경우 이 상황이 더 바람직합니다.

그림 9-4는 department_id 열이 인덱스된 자식 테이블 employees를 보여줍니다. 트랜잭션이 departments에서 부서 280을 삭제합니다. 이 삭제는 "인덱스되지 않은 외래 키와 잠금"에서 설명된 시나리오와 같이 employees 테이블에 대한 전체 테이블 잠금을 발생시키지 않습니다.

그림 9-4 인덱스된 외래 키와 잠금 메커니즘

그림 9-4 설명
그림 9-4 "인덱스된 외래 키와 잠금 메커니즘" 설명

자식 테이블이 ON DELETE CASCADE를 지정하면 부모 테이블에서 삭제가 자식 테이블에서 삭제로 이어질 수 있습니다. 예를 들어, 부서 280의 삭제는 삭제된 부서에 있는 직원에 대한 기록을 employees에서 삭제할 수 있습니다. 이 경우, 삭제 후 자식 테이블에서 행을 삭제하는 것과 동일한 대기 및 잠금 규칙이 적용됩니다.

참조: "외래 키 제약 조건"
"인덱스 소개"

DDL Locks

데이터 사전(DDL) 잠금은 진행 중인 DDL 작업이 객체를 참조하거나 객체에 대해 작동하는 동안 스키마 객체의 정의를 보호합니다.

DDL 작업 중에는 수정되거나 참조된 개별 스키마 객체만 잠깁니다. 데이터베이스는 전체 데이터 사전을 절대 잠그지 않습니다.

Oracle Database는 필요한 경우 모든 DDL 트랜잭션을 대신하여 자동으로 DDL 잠금을 획득합니다. 사용자는 DDL 잠금을 명시적으로 요청할 수 없습니다. 예를 들어, 사용자가 저장 프로시저를 생성하면 Oracle Database는 프로시저 정의에서 참조된 모든 스키마 객체에 대해 자동으로 DDL 잠금을 획득합니다. DDL 잠금은 프로시저 컴파일이 완료되기 전에 이러한 객체가 변경되거나 삭제되는 것을 방지합니다.

Exclusive DDL Locks

배타 DDL 잠

금은 다른 세션이 DDL 또는 DML 잠금을 획득하지 못하도록 합니다.

대부분의 DDL 작업은 동일한 스키마 객체를 수정하거나 참조할 수 있는 다른 DDL 작업과의 파괴적 간섭을 방지하기 위해 리소스에 대해 배타 DDL 잠금을 요구합니다. 예를 들어, ALTER TABLE이 열을 추가하는 동안 DROP TABLE이 테이블을 삭제할 수 없으며 그 반대도 마찬가지입니다.

배타 DDL 잠금은 DDL 문 실행과 자동 커밋 기간 동안 지속됩니다. 배타 DDL 잠금을 획득하는 동안 다른 작업에 의해 스키마 객체에 대해 DDL 잠금이 유지되면, 이전 DDL 잠금이 해제될 때까지 대기한 후 진행됩니다.

참조: "공유 DDL 잠금"에서는 파괴적 간섭을 방지하기 위해 배타 잠금이 필요하지 않은 상황을 설명합니다.

Share DDL Locks

리소스에 대한 공유 DDL 잠금은 충돌하는 DDL 작업과의 파괴적 간섭을 방지하지만, 유사한 DDL 작업에 대한 데이터 동시성을 허용합니다.

예를 들어, CREATE PROCEDURE 문이 실행될 때, 해당 트랜잭션은 참조된 모든 테이블에 대해 공유 DDL 잠금을 획득합니다. 다른 트랜잭션은 동일한 테이블을 참조하는 프로시저를 동시에 생성하고 동일한 테이블에 대해 공유 DDL 잠금을 동시에 획득할 수 있지만, 참조된 테이블에 대해 배타 DDL 잠금을 획득할 수 없습니다.

공유 DDL 잠금은 DDL 문 실행과 자동 커밋 기간 동안 지속됩니다. 따라서, 공유 DDL 잠금을 보유한 트랜잭션은 참조된 스키마 객체의 정의가 트랜잭션 동안 일정하게 유지됨을 보장받습니다.

Breakable Parse Locks

파싱 잠금은 SQL 문 또는 PL/SQL 프로그램 유닛이 참조하는 각 스키마 객체에 대해 유지됩니다.

파싱 잠금은 관련 공유 SQL 영역이 참조된 객체가 변경되거나 삭제되면 무효화될 수 있도록 획득됩니다. 파싱 잠금은 충돌하는 DDL 작업을 허용하기 위해 파괴 가능(parse lock)이라고 불립니다.

파싱 잠금은 SQL 문 실행의 파싱 단계 동안 공유 풀에서 획득됩니다. 이 잠금은 해당 문에 대한 공유 SQL 영역이 공유 풀에 남아 있는 한 유지됩니다.

참조: "공유 풀"

System Locks

Oracle Database는 내부 데이터베이스 및 메모리 구조를 보호하기 위해 다양한 유형의 시스템 잠금을 사용합니다. 이러한 메커니즘은 사용자가 제어할 수 없기 때문에 접근할 수 없습니다.

Latches

래치는 공유 데이터 구조, 객체 및 파일에 대한 다중 사용자 접근을 조정하는 간단한 저수준 직렬화 메커니즘입니다.

래치는 여러 프로세스가 접근할 때 공유 메모리 리소스를 손상되지 않도록 보호합니다. 구체적으로, 래치는 다음 상황에서 데이터 구조를 보호합니다:

  • 여러 세션에 의한 동시 수정
  • 한 세션이 수정 중인 다른 세션에 의해 읽히는 경우
  • 접근 중인 동안 메모리 해제(노후화)

일반적으로 하나의 래치는 SGA의 여러 객체를 보호합니다. 예를 들어, DBW 및 LGWR와 같은 백그라운드 프로세스는 데이터 구조를 생성하기 위해 공유 풀에서 메모리를 할당합니다. 이 메모리를 할당하기 위해 이러한 프로세스는 두 프로세스가 동시에 공유 풀을 검사하거나 수정하지 않도록 직렬화를 통해 공유 풀 래치를 사용합니다. 메모리가 할당된 후 다른 프로세스는 파싱에 필요한 라이브러리 캐시와 같은 공유 풀 영역에 접근해야 할 수 있습니다. 이 경우, 프로세스는 전체 공유 풀이 아닌 라이브러리 캐시만 래치합니다.

행 잠금과 같은 큐 래치와 달리, 래치는 세션이 대기하도록 허용하지 않습니다. 래치가 사용 가능해지면, 래치를 요청하는 첫 번째 세션이 독점 접근 권한을 획득합니다. 래치 스피닝 현상은 프로세스가 래치를 루프 내에서 반복적으로 요청할 때 발생하며, 래치 슬리핑은 프로세스가 래치 요청을 갱신하기 전에 CPU를 해제할 때 발생합니다.

일반적으로, Oracle 프로세스는 데이터 구조를 조작하거나 검사하는 동안 매우 짧은 시간 동안 래치를 획득합니다. 예를 들어, 단일 직원의 급여를 처리하는 동안 데이터베이스는 수천 개의 래치를 획득하고 해제할 수 있습니다. 래치의 구현은 운영 체제에 종속적이며, 특히 프로세스가 래치를 대기하는지 여부와 대기 시간에 따라 다릅니다.

래치 증가란 동시성 감소를 의미합니다. 예를 들어, 과도한 하드 파싱 작업은 라이브러리 캐시 래치에 대한 경쟁을 유발합니다. V$LATCH 뷰에는 각 래치에 대한 사용 통계가 자세히 포함되어 있으며, 각 래치가 요청되고 대기한 횟수를 포함합니다.

참조: "SQL 파싱"
V$LATCH에 대해 배우려면 Oracle Database Reference를 참조하십시오.
대기 이벤트 통계에 대해 배우려면 Oracle Database Performance Tuning Guide를 참조하십시오.

Mutexes

상호 배제 객체(뮤텍스)는 메모리에서 객체가 노후화되거나 동시 프로세스에 의해 손상되지 않도록 방지하는 저수준 메커니즘입니다. 뮤텍스는 래치와 유사하지만, 래치가 일반적으로 여러 객체를 보호하는 반면, 뮤텍스는 단일 객체를 보호합니다.

뮤텍스는 다음과 같은 여러 이점을 제공합니다:

  • 뮤텍스는 경쟁 가능성을 줄일 수 있습니다.
  • 래치가 여러 객체를 보호하기 때문에 프로세스가 이러한 객체에 동시 접근하려고 할 때 병목 현상이 발생할 수 있습니다. 개별 객체에 대한 접근을 직렬화함으로써 뮤텍스는 가용성을 증가시킵니다.
  • 뮤텍스는 래치보다 적은 메모리를 소비합니다.
  • 공유 모드에서 뮤텍스는 여러 세션에 의한 동시 참조를 허용합니다.

Internal Locks

내부 잠금은 래치와 뮤텍스보다 더 복잡하고 높은 수준의 메커니즘으로 다양한 목적을 수행합니다.

데이터베이스는 다음 유형의 내부 잠금을 사용합니다:

  • 사전 캐시 잠금: 이 잠금은 매우 짧은 기간 동안 유지되며 사전 캐시의 항목이 수정되거나 사용되는 동안 항목에 대해 유지됩니다. 이 잠금은 구문 분석 중에 일관되지 않은 객체 정의를 보지 않도록 보장합니다. 사전 캐시 잠금은 공유 또는 배타적일 수 있습니다. 공유 잠금은 구문 분석이 완료되면 해제되고, 배타 잠금은 DDL 작업이 완료되면 해제됩니다.
  • 파일 및 로그 관리 잠금: 이러한 잠금은 다양한 파일을 보호합니다. 예를 들어, 내부 잠금은 제어 파일을 보호하여 한 번에 하나의 프로세스만 변경할 수 있도록 합니다. 다른 잠금은 온라인 리두 로그 파일의 사용 및 아카이빙을 조정합니다. 데이터 파일은 여러 인스턴스가 데이터베이스를 공유 모드로 마운트하거나 하나의 인스턴스가 독점 모드로 마운트하도록 잠깁니다. 파일 및 로그 잠금은 파일 상태를 나타내므로, 이러한 잠금은 반드시 오랫동안 유지됩니다.
  • 테이블스페이스 및 undo 세그먼트 잠금: 이러한 잠금은 테이블스페이스 및 undo 세그먼트를 보호합니다. 예를 들어, 데이터베이스에 접근하는 모든 인스턴스는 테이블스페이스가 온라인인지 오프라인인지에 대해 동의해야 합니다. Undo 세그먼트는 하나의 데이터베이스 인스턴스만 세그먼트에 기록할 수 있도록 잠깁니다.

참조: "데이터 사전 캐시"

Overview of Manual Data Locks

Oracle Database의 기본 잠금 메커니즘을 수동으로 재정의할 수 있습니다.

Oracle Database는 데이터 동시성, 데이터 무결성, 문장 수준 읽기 일관성을 보장하기 위해 잠금을 자동으로 수행합니다. 그러나 기본 잠금을 재정의하는 것이 유용한 상황이 있습니다. 예를 들어:

  • 애플리케이션이 트랜잭션 수준의 읽기 일관성 또는 반복 가능한 읽기를 요구하는 경우

이 경우 쿼리는 트랜잭션이 지속되는 동안 일관된 데이터를 생성해야 하며, 다른 트랜잭션의 변경 사항을 반영하지 않아야 합니다. 명시적 잠금, 읽기 전용 트랜잭션, 직렬화 가능한 트랜잭션을 사용하거나 기본 잠금을 재정의하여 트랜잭션 수준의 읽기 일관성을 달성할 수 있습니다.

  • 애플리케이션이 트랜잭션이 다른 트랜잭션이 완료되기를 기다릴 필요가 없도록 리소스에 대한 독점 접근을 요구하는 경우

세션 또는 트랜잭션 수준에서 Oracle Database의 자동 잠금을 재정의할 수 있습니다. 세션 수준에서 세션은 ALTER SESSION 문을 사용하여 필요한 트랜잭션 격리 수준을 설정할 수 있습니다. 트랜잭션 수준에서 다음 SQL 문을 포함하는 트랜잭션은 Oracle Database의 기본 잠금을 재정의합니다:

  • SET TRANSACTION ISOLATION LEVEL 문
  • LOCK TABLE 문(테이블을 잠그거나 뷰와 함께 사용될 때 기본 테이블을 잠급니다)
  • SELECT ... FOR UPDATE 문

위의 문에 의해 획득된 잠금은 트랜잭션이 종료된 후 또는 저장점으로 롤백되면 해제됩니다.

어느 수준에서든 Oracle Database 기본 잠금이 재정의되면 데이터베이스 관리자 또는 애플리케이션 개발자는 재정의된 잠금 절차가 올바르게 작동하는지 확인해야 합니다. 잠금 절차는 다음 기준을 충족해야 합니다: 데이터 무결성이 보장되고, 데이터 동시성이 수용 가능하며, 교착 상태가 발생하지 않거나 적절히 처리됩니다.

참조:

  • LOCK TABLE 및 SELECT에 대한 설명은 Oracle Database SQL Language Reference를 참조하십시오.
  • 테이블을 수동으로 잠그는 방법에 대해 배우려면 Oracle Database Development Guide를 참조하십시오.

Overview of User-Defined Locks

Oracle Database 잠금 관리 서비스를 사용하여 특정 애플리케이션을 위해 사용자 정의 잠금을 정의할 수 있습니다.

예를 들어, 파일 시스템에서 메시지 로그에 대한 접근을 직렬화하기 위해 잠금을 생성할 수 있습니다. 예약된 사용자 잠금은 Oracle Database 잠금과 동일하므로 교착 상태 감지를 포함한 모든 Oracle Database 잠금 기능을 갖추고 있습니다. 사용자 잠금은 UL 접두사를 사용하여 식별되므로 Oracle Database 잠금과 충돌하지 않습니다.

Oracle Database 잠금 관리 서비스는 DBMS_LOCK 패키지의 절차를 통해 사용할 수 있습니다. PL/SQL 블록에 다음과 같은 문을 포함할 수 있습니다:

  • 특정 유형의 잠금을 요청합니다.
  • 동일한 인스턴스 또는 다른 인스턴스의 다른 절차에서 인식할 수 있는 고유한 이름을 잠금에 부여합니다.
  • 잠금 유형을 변경합니다.
  • 잠금을 해제합니다.

참조:

  • Oracle Database 잠금 관리 서비스에 대한 자세한 내용은 Oracle Database Development Guide를 참조하십시오.
  • DBMS_LOCK에 대한 정보는 Oracle Database PL/SQL Packages and Types Reference를 참조하십시오.

Footnote Legend

각주 1:
분산 2단계 커밋을 처리할 때, 데이터베이스는 특수한 상황에서 일시적으로 읽기 접근을 방지할 수 있습니다. 구체적으로, 쿼리가 준비 단계와 커밋 단계 사이에 시작되어 커밋 전에 데이터를 읽으려고 시도하면 데이터베이스는 읽기 일관성을 보장하기 위해 잠금을 행 수준에서 블록 수준으로 확장할 수 있습니다.

profile
비전공 개발 공부 이야기

0개의 댓글