CH3.트랜잭션과 일관성

트랜잭션과 언두

  • 데이터베이스를 생성할 때 반드시 언두 테이블스페이스를 생성해야 한다.
    (RAC의 경우, 각 인스턴스 당 하나의 언두 테이블스페이스가 필요)
  • 언두 테이블스페이스 내에 몇 개의 언두 세그먼트를 자동으로 생성.(undo_management=auto일 경우)
    (작업 부하에 따라 자동으로 언두 세그먼트를 추가, 확장, 축소 또는 drop)
  • 트랜잭션 관리는 언두 세그먼트로부터 시작하고 언두 세그먼트 중심으로 수행.
  • 언두 세그먼트 헤더 블록은 언두 세그먼트의 첫 번째 블록.
  • 다른 유형의 세그먼트 헤더 블록 내에서 볼 수 잇는 다수의 표준 구조 포함
  • transaction table(최근 트랜잭션들을 식별할 수 있는 리스트)과
    transaction table control section(트랜잭션 테이블의 상태와 내용을 자세하게 설명한 부분)과 같은 매우 특별한 구조도 포함.
  • 트랜잭션 테이블 내에는 제한된 수의 엔트리가 존재.
  • 언두 테이블스페이스 내에는 제한 된 수의 언두 세그먼트가 존재.
  • 비교적 적은 수의 최근 트랜잭션들에 대해서만 세부사항을 기록할 수 있으며, 트랜잭션 테이블 엔트리들은 지속적으로 재사용되어야만 한다.
  • 트랜잭션 테이블 내의 엔트리를 재사용할 때마다, 해당 엔트리에 대한 wrap# 수치가 증가.
    {tip}
  • 인스턴스가 재기동될 때마다 wrap#는 초기화되지 않는다.
  • 일반적인 원칙은, 데이터베이스 내에 저장되는 카운터 유형의 정보는 인스턴스가 재기동될 때 초기화되지 않는다.
  • 모든 언두 세그먼트 내의 모든 엔트리는 자신의 wrap#를 가진다.
    {tip}

트랜잭션의 시작과 끝

  • 트랜잭션 시작
    • 언두 세그먼트 획득 -> 트랜잭션 테이블 내의 엔트리 획득 -> wrap# 증가 -> state 컬럼값 "active"(value 10)로 변경. -> 다른 몇개의 컬럼 값을 변경.
      (이것은 데이터베이스 블록에 대한 변경이므로) 리두 체인지 벡터 (OP code, 5.2) 생성하고, 최종 적으로 리두 로그 파일에 기록.
  • 트랜잭션 종료
    • state 컬럼을 "free"(value 9)로 원복 -> 엔트리 내에 존재하는 몇 개의 다른 컬럼을 변경.(current SCN을 scn컬럼에 기록)
      리두 체인지 벡터 (OP code, 5.4)를 생성하고 리두 로그 파일에 기록.
      (이 순간 해당 세션은 lgwr에게 로그 버퍼의 현재 내용을 디스크로 기록하라고 요청, lgwr이 기록을 완료하는 것을 대기함으로서, 변경사항 보호)
      {tip}
  • 인터넷이나 오라클 문서 내에서 lgwr이 "커밋 레코드를 생성" 한다는 설명을 종종 발견할 것이다. 하지만 그러한 동작은 존재하지 않는다.
  • 세션은 트랜잭션을 종료할 때, 데이터베이스 블록(특히, 트랜잭션 테이블의 슬롯을 보유한 언두 세그먼트 헤더 블록)을 변경하고, 이 블록 변경은
    리두 체인지 벡터 생성을 필요로 하며, 리두 체인지 벡터는 로그 버퍼로 복사된다. 이 체인지 벡터를 "the commit record"라고 부르기도 한다.
  • Commit record에 대한 하나의 특별한 점은, 해당 레코드가 로그 버퍼 내로 복사되면, 세션은 lgwr을 호출하여
    로그 버퍼의 현재 내용을 디스크로 기록하라고 요청하며, 기록이 완료될 때까지 대기한다는 점(6장에서 설명)
    {tip}
  • 트랜잭션은 트랜잭션 테이블 내에서 획득한 엔트리에 의해 정의.
    • "언두 세그먼트 번호"."트랜잭션 테이블 내 언트리의 인덱스 번호"."엔트리의 가장 최근 wrap#"로 구성된 transaction ID를 부여.
    • dba_rollback_segs 뷰를 통해 segment_id(언두 세그먼트 번호)를 이용하여 조회 가능.
    • transaction ID는 v$lock, v$transaction 뷰에서도 확인 가능.
    • 필자는 트랜잭션 테이블 내의 "엔트리"라고 지칭, 뷰에서는 그것을 슬롯(slot)이라 지칭.
      엔트리와 슬롯은 동일한 의미로 사용.

트랜잭션 테이블

컬럼명설명
index트랜잭션 테이블 내의 로우의 순번이며 transcation id의 구성요소로 사용.
일반적으로 transaction table slot 번호로 알려져 있다
(블록 내에 물리적으로 저장되는 값은 아니며, 블록을 덤프 할 때 슬롯 위치에 의해 얻어지는 값이다)
state엔트리의 상태:9는 inactive, 10은 active를 의미
cflags슬롯을 사용하는 트랜잭션의 상태를 보여주는 bit flag:
0x0 no transaction, 0x10 dead transaction, 0x80 active transaction
(0x90 - dead 후에 rollback 수행 중)
wrap#슬롯이 재사용된 횟수를 위한 카운터, transaction id의 구성요소
uel이 슬롯이 active상태로 변경된 후에, 다음 번으로 사용될 트랜잭션 테이블 슬롯에 대한 포인터.
이것은 , 새로운 세그먼트 내에서 매우 잘 정렬되어있으나 트랜잭션이 수행됨에 따라,
결국에는 상당히 랜덤한 링크드 리스트가 될 것이다.
scn커밋 된 트랜잭션에 대한 commit SCN(rollback은 최종적으로 commit으로 완료되므로, rollback 종료에 대한 commit SCN으로도 사용된다)
대부분 오라클 버전에서, 이 컬럼은 트랜잭션이 active일 때는 start SCN으로도 사용된다.
dba언두 레코드를 기록하기 위해 트랜잭션이 마지막으로 사용한 언두 블록의 주소(Data Block Address).
이것을 이용하여(특히 crash recovery시) 트랜잭션에 의해 생성된 마지막 언두 레코드를 찾음으로써, 롤백 작업의 시작 위치를 알 수 있다
nub해당 트랜잭션에 의해 지금까지 사용된 언두 블록의 수(트랜잭션 롤백을 수행하는 동안에는 이 숫자가 감소하는 것을 볼 수 있다)
cmtCommit 시점과 가장 가까운 초. 1970년 1월 1일 0시(UTC)부터의 시간을 초로 환산한다.
  • x$ktuxe 통해서도 확인 가능. (cmt 컬럼은 제공하지 않는다)
    (가장 최근에 수행된 N(언두 세그먼트 개수) * 34(11g인 경우, 트랜잭션 테이블 슬롯의 개수)개의 트랜잭션에 대한 중요한 정보를 액세스 할 수 있다.)
    해당 구조를 조회하면, 실질적으로 각 언두 세그먼트내의 각 언두 세그먼트 헤더 블록을 방문한다.
    • 트랜잭션의 슬롯 번호
    • 트랜잭션의 커밋 여부, 또는 active 여부를 나타내는 표시
    • 커밋 된 트랜잭션에 대한 SCN
    • 트랜잭션에 의해 생성된 가장 최근의 언두 레코드를 찾을 수 있는 위치에 대한 정보
      {tip}
  • 트랜잭션 중간에 savepoint들을 선언하고 특정 savepoint로의 롤백이 가능.
  • 이를 위해 사용자 세션은 savepoint call이 발생하기 직전에 생성된 마지막 언두 레코드의 주소를 세션 메모리 내에
    존재하는 current savepoint 리스트에 유지해야 한다.
    {tip}

언두 블록 다시 보기

  • 여러가지 면에서 언두 블록과 일반적인 데이터 블록은 유사.
  • 몇 개의 컨트롤 정보와 메타 데이터를 가진 헤더 섹션이 있으며, 아이템들의 위치 정보를 가진 row directory가 존재.
  • 아이템(이경우에는, 언두 레코드)들은 블록의 맨 아래부터 차곡차곡 쌓이며, 이로 인해 블록의 중간에 free 공간이 존재.
  • 언두 레코드는 변경되지 않는다. 언두 레코드가 언두 블록 내에 저장되면 항상 동일한 장소에 있게 된다.
    {tip}
  • 오라클이 언두 블록을 재사용할 때, 이전 내용에 대해서는 신경 쓰지 않는다. (p.48 newed???)
  • 언두 블록을 재사용하기 전에 디스크로부터 언두 블록을 읽어 들이는 작업을 할 필요가 없다.
    단지, 버퍼를 할당하고 버퍼 캐시 내의 새로운 빈 블록을 포맷한다. (이 작업을 블록 newing이라 한다)
  • 이 매카니즘은 truncate된 테이블에 새로운 로우를 입력할 때도 동일한 효과가 발생.(첫 번째 insert에 대해서만 동작)
  • Flashback Database가 활성화되어 있다면, 오라클은 대개, 해당 블록의 이전 버전을 flashback log로 복사할 필요가 있다고 결정.
    (이 경우에는 newing 전에 언두 블록을 읽어야 한다. "physical reads for flashback new" 성능통계로 확인가능)
    {tip}
  • 하나의 언두 블록이 다수의 트랜잭션으로부터 발생한 언두 레코드를 저장할 수 있다.
    • 트랜잭션 커밋 시에 언두 블록 내에 충분한 빈 공간이 존재한다면,
      해당 블록은 언두 세그먼트 헤더 내의 free block pool이라고 불리는 리스트에 추가.
    • 해당 언두 세그먼트를 사용하는 다음 번 트랜잭션은, pool로부터 해당 블록을 획득한 후 나머지 공간을 사용 할 수 있다.
    • 다수의 active 트랜잭션들이 동시에 동일한 언두 블록에 기록할 수는 없으나,
      다수의 트랜잭션들이 동일한 언두 블록에 순차적으로 기록하는 것은 가능하다.
      (언두 레코드에 슬롯 번호를 같이 저장하기 때문
      ==> 다른 트랜잭션이 같은 슬롯, 같은 블록을 사용하는 일이 발생한다면???? 다른 속성값이 있겠지??? )