본문 바로가기
스터디/오라클 성능고도화 원리와 해법1

CH2. 트랜잭션과 LOCK - 01.트랜잭션 동시성 제어, 02.트랜잭션 수준 읽기 일관성

by 취미툰 2019. 12. 25.
반응형

01. 트랜잭션 동시성 제어

(1) 동시성 제어
예전에는 주로 상담원을 통해 온라인 주문을 받았고, 시스템 사용법을 교육받은 숙련된 사용자에 의해 데이터 조작이 이루어졌습니다. 요즘에는 사용자가 직접 자신의 주문을 처리하는 환경이 되었고 생각지 못했던 일들이 훨씬 자주 발생하게 됩니다. 동시 접속자 수도 이전과 비교할 수 없을 정도로 많아졌기 때문에 동시성에 대한 이슈는 훨씬 더 중요해졌습니다.
동시성 제어(concurrency control)이란 동시에 실행되는 트랜잭션 수를 최대화하면서도 입력, 수정,삭제,검색 시 데이터의 무결성이 유지될 수 있도록 노력하는 것을 말합니다. 여러 개 트랜잭션이 동시에 수행될 때, 데이터베이스 애플리케이션은 이들 트랜잭션이 서로 간섭을 일으키는 현상을 최소화하면서 데이터의 일관성과 무결성이 보장되도록 개발되어야 합니다.
- 동시성(concurrency):다중 사용자가 같은 데이터를 동시에 액세스
- 일관성(consistency): 자신이 발생시킨 변경 사항과 다른 트랜잭션의 변경 사항을 포함해 일관성 있는 상태로 데이터를 제공
동시성 제어가 어려운 이유는 동시성과 일관성이 서로 반비례관계에 있기 때문입니다. 동시성을 높이려고 lock의 사용을 최소화하면 읽기 일관성을 유지하기 어렵고 일관성을 높이려고 lock의 사용을 많이 하면 동시성이 떨어지게 됩니다.

(2) 트랜잭션이란?
트랜잭션이란 여러 개의 수정 작업이 하나의 작업처럼 전부 처리되거나 아예 전부 처리가 안되도록 하는 것인데 이러한 일의 최소 단위입니다.
예를 들어 은행 업무의 계좌이체 트랜잭션은 하나의 예금 계좌에서 인출하여 다른 예금 계좌에 입금하는데, 이 두 작업은 하나의 단위로 함께 수행되어야 합니다.
온라인 쇼핑몰에서 이루어지는 주문 트랜잭션은 상품정보를 확인하고 각종 주문정보를 입력하고 나서 최종 결제를 완료하는 순간까지를 하나의 트랜잭션으로 정의하는 것이 타당합니다.

(3) 트랜잭션의 특징(ACID)
트랜잭션은 4가지 중요한 특징을 갖습니다.
-원자성(Atomicity) : 더 이상 분해가 불가능한 업무의 최소 단위를 말합니다.
-일관성(Consistency) : 트랜잭션이 그 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 변환합니다.
-격리성(Isolation) : 트랜잭션이 실행 중에 생성하는 연산의 중간 결과는 다른 트랜잭션이 접근할 수 없습니다.
-영속성(Durability) : 트랜잭션이 일단 실행을 성공적으로 완료하고 나면 그 결과는 데이터베이스에 영속적으로 저장됩니다.
트랜잭션의 처리결과가 데이터의 일관성을 해치지 않도록 하려면 트랜잭션의 순차적 진행을 보장할 수 있는 직렬화 장치가 필요하고 그것이 Lock 메커니즘입니다. 오라클은 그것뿐 아니라 undo데이터를 활용한 독특한 읽기 일관성 모델을 통해 더 높은 수준의 읽기 일관성을 보장하고 있습니다.

02. 트랜잭션 수준 읽기 일관성

(1) 트랜잭션 수준 읽기 일관성이란?
문장 수준의 읽기 일관성은 쿼리가 시작된 시점을 기준으로 데이터를 일관성 있게 읽어 들이는 것을 말합니다. 트랜잭션 수준 읽기 일관성은 트랜잭션이 시작된 시점을 기준으로 일관성 있게 데이터를 읽어들이는 것을 말합니다. 트랜잭션이 진행되는 동안 다른 트랜잭션에 의해 변경사항이 발생하더라도 이를 무시하고 계속해서 일관성 있는 데이터를 보고자 하는 업무 요건이 있을 수 있는데, 이때 물론 트랜잭션이 진행되는 동안 자신이 발생시킨 변경사항은 읽을 수 있어야 합니다.

(2) 트랜잭션 고립화 수준
ANSI/ISO SQL standard(SQL92)에서 정의하고 있는 네 가지 트랜잭션 고립화 수준을 간단히 요약하겠습니다.

level 0 (= read uncommitted)
-트랜잭션에서 처리 중인, 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽는 것을 허용
-Dirty read, non-repeatable read, phantom read현상 발생
- oracle은 이 레벨을 지원하지 않음

level 1(=read committed)
-Dirty Read 방지 : 트랜잭션이 커밋되어 확정된 데이터만 읽는 것을 허용
- 대부분의 DBMS가 채택하고 있는 일관성 모드
- non-repeatable read, phantom read현상은 여전히 발생
- oracle은 쿼리 시작 시점의 undo 데이터를 제공하는 것으로 구현
- db2, sql server, sybase의 경우 읽기 공유 lock을 이용해서 구현. 하나의 레코드를 읽을 때 lock을 설정하고 해당 레코드를 빠져나가는 순간 lock 해제

level 2(=repreatable read)
-선행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지 후행 트랜잭션이 갱신하거나 삭제하는 것을 불허함으로써 같은 데이터를 두 번 쿼리 했을 때 일관성 있는 결과를 리턴
-phantom read현상은 여전히 발생
-oracle은 for update 절을 이용해 구현 가능
- db2, sql server의 경우 고립화 수준을 repeatable read로 변경하면 읽은 데이터에 걸린 공유 lock을 커밋할 때까지 유지하는 방식으로 구현

level 3(=serializable)
- 선행 트랜잭션이 읽은 데이터를 후행 트랜잭션이 갱신하거나 삭제하지 못할 뿐만 아니라 중간에 새로운 레코드를 삽입하는 것도 막아줌
- 완벽한 읽기 일관성 모드를 제공
- set transaction isolation level serializable;명령어로 사용 가능

(3) Dirty Read(=Uncommitted Dependency)
아직 커밋되지 않은 수정 중인 데이터를 다른 트랜잭션에서 읽을 수 있도록 허용할 때 발생합니다. 만약 dirty read를 허용하면 상황에서 아래 문장이 수행되는 도중에 다른 트랜잭션에 의해 특정 계좌의 잔고가 변경된다면 쿼리의 최종 결과 값이 비일관성 상태에 놓이게 될 수 있습니다. 왜냐하면 어떤 이유에서든 트랜잭션은 롤백될 수 있기 때문입니다. 대부분의 DBMS는 커밋된 데이터만 읽을 수 있도록 허용하기 때문에 dirty read현상은 발생하지 않습니다. 오라클은 이 수준으로 트랜잭션 고립화 수준을 낮추는 방법을 아예 제공하지 않고 있습니다.

(4) Non-Repeatable Read(=Inconsistent Analysis)
한 트랜잭션 내에서 같은 쿼리를 두 번 수행할 때, 그 사이에 다른 트랜잭션이 값을 수정 또는 삭제 함으로써 두 쿼리의 결과가 상이하게 나타나는 비일관성이 발생하는 것을 말합니다.

tx1 tx2

1.select 당월 주문금액 into :amt from 고객 

where 고객번호 = 123;

 
 

1.update 고객 set 당월 주문금액 = 60000, 등급 = ‘A’

where 고객번호 = 123;

  2.commit;
2.if :amt >= 50000 then
update 고객
set 등급 = ‘A’
where 고객번호 = 123;
else
update 고객
set 등급 = ‘B’
where 고객번호 = 123;
end if;
 
3.commit;  

tx1시점에서 당월 주문금액은 40,000원이라고 가정할 때, 쿼리 시작할 때의 :amt는 40000이 저장됩니다. 두 트랜잭션 수행을 종료하고 나면 tx2트랜잭션에 의해 60,000원으로 변경되었음에도 고객등급이 B로 하향 조정되는 결과를 가져옵니다. Non-Repeatable Read 현상 때문에 tx2의 등급 조정이 상실되는 Lost Update가 발생한 것입니다. 이를 방지하기 위해서는 tx1이 1번 select 문장을 실행할 때 for update 절을 추가해 주어야 합니다. 그러면 123번 고객 레코드에 lock이 걸리므로 tx2 트랜잭션은 블로킹됐다가 tx1이 커밋하는 시점 이후에 진행을 계속하게 됩니다.

(5) Phantom Read
한 트랜잭션 안에서 일정 범위의 레코드를 을 두 번 이상 읽을 때, 첫 번째 쿼리에서 없던 유령 레코드가 두 번째 쿼리에서 나타나는 현상을 말합니다. 트랜잭션 도중에 새로운 레코드가 삽입되는 것을 허용하기 때문에 나타나는 현상입니다.

 

tx1 tx2
1.insert into 지역별고객
select 지역, count(*)
from 고객
group by 지역;
 
  1.insert into 고객
(고객번호,이름,지역,연령대,...)
values (:a,:b,:c,:d,...);
  2.commit;
2.insert into 연령대별고객
select 연령대, count(*)
from 고객
group by 연령대;
 
3.commit;  

tx1 트랜잭션이 지역별 고객과 연령대 별고객을 연속해서 집계하는 도중에 새로운 고객이 tx2트랜잭션에 의해 등록되었습니다. 그 결과 지역별고객과 연령대별 고객 두 집계 테이블을 통해 총 고객 수를 조회하면 서로 결과 값이 다른 불일치 상태에 놓이게 됩니다. 이런 phantom read현상을 방지하려면 tx1이 1번 문장을 수행하기 전에 아래 문장을 통해 트랜잭션 고립화 수준을 레벨 3으로 올려주어야 합니다.
set transaction isolation level serializable;
트랜잭션 고립화 수준을 레벨 3으로 높이면 sql server에서는 lock을 통해 t2 시점에 새로운 고객이 추가되는 것을 막아버립니다. 따라서 고립화 수준을 높이면 데이터 일관성을 확보되지만 동시성이 현격히 저하되는 결과를 초래합니다. 오라클은 lock을 전혀 사용하지 않을 상태에서 tx1 1번, 2번 쿼리 모두 SCN확인 과정을 통해 t1 시점에 존재했던 고객만을 대상으로 집계를 수행하므로 동시성을 저하시키지 않으면서 일관성을 유지합니다.

트랜잭션 고립화 수준을 높이면 일관성은 향상되지만 더 넓은 범위의 lock을 더 오랫동안 유지하는 방식을 사용하므로 동시성은 저하된다. 하지만 오라클은 트랜잭션 고립화 수준을 높이더라도 lock을 사용하지 않으므로 동시성이 저하되지는 않습니다.

SERIALIZABLE_ABORTS
오라클은 for update 절을 사용하지 않는 한 절대 select문에 lock을 사용하지 않기 때문에 레벨을 상향 조정하더라도 이전과 동일한 동시성을 보장합니다. 높은 일관성 수준에서도 높은 동시성을 제공할 수 있는 이유는, Undo 데이터를 활용하기 때문입니다. 그렇지만 세상만사에 공짜는 없는 법이어서 만일 트랜잭션 레벨에서 일관성 있는 데이터를 제공할 수 없는 상황이 발생하면 다른 DBMS에는 없는 에러를 던지게 됩니다(ORA-08177)

 

 

 

반응형

댓글