'readuncommitted'에 해당되는 글 2건

  1. 2017.06.12 SQL Server DeadLock 1편
  2. 2017.06.04 .NET IsolationLevel에 대한 고찰 5

개발자가 할줄은 알지만 은근히 모르는 DB개발과 관련된 글을 쓰려고 합니다. 저도 SP를 실제 프로젝트에 사용한 것이 얼마 안되었고 프로그램 개발에 더 많은 시간을 투자하였습니다. 그래서인지 신선한?! DB개발을 하니 신입때 첫 프레임워크를 사용하는 느낌입니다.

구글에서 DeadLock과 관련된 글들을 보고 제가 이해한 것을 기반으로 설명을 해보려고 합니다. 틀린 부분이 있다면 댓글을 달아주시면 좋습니다. (댓글이 없어서 사실 아무말이나 좋습니다 ㅎㅎ)

먼저 DeadLock이기 때문에 트랜잭션과 Lock 이야기로 시작해보겠습니다.
이전 글에서 이야기 했듯이 트랜잭션(IsolationLevel)에 대해서 일반적으로 ReadUncommitted로 설정을 하고 사용할 것이라고 하였습니다. 그렇게 생각하는 이유는 ReadCommitted나 RepeatableRead, Serializable 등등은 만족할만한 성능을 얻기가 힘들며 Lock을 잠그는 시간이 길어지면서 DeadLock이 발생할 확률도 높습니다.

당연한 사실이지만 트랜잭션이 발생한 상태에서 Lock도 발생하는 것입니다. 트랜잭션이 없다면 Lock도 없습니다. 그렇다고 트랜잭션이 있다고 Lock이 꼭 있는건 아닙니다. 바로 트랜잭션 IsolationLevel 옵션을 ReadUncommitted를 사용(일반적으로 Select구문일 경우)할 때입니다. ReadUncommitted는 그 이름처럼 다른 트랜잭션에서 커밋이 되기 전의 데이터를 읽을 수 있습니다. Lock없이 읽기 때문에 성능적인 측면이나 DeadLock이 발생하는 것을 방지하려는 의도로 사용되는 것입니다. 다만 커밋되기 전의 데이터 읽게 되므로 데이터의 무결성이 깨질 수 있습니다.
=> 위의 Lock이 없다는 말은 잘못된 표현이네요. 아무리 짧은 SQL 구문이라도 Lock이 발생합니다. 저 말의 의미는 실행시간이 매우 짧은 Select 구문일 경우 트랜잭션 내에서 ReadUncommitted일 경우 실행 후 Lock이 해제된다는 사실에 대한 강조하려다 보니 잘못된 표현을 한 것으로 보입니다. (2017-08-20)

하나 더 살펴보아야 할 IsolationLevel은 Snapshot입니다. 아래처럼 DB에 설정이 필요합니다.

-- 1. Snapshot 허용
ALTER DATABASE [DB명]
SET ALLOW_SNAPSHOT_ISOLATION ON

-- 2. ReadCommitted 옵션을 사용할 경우 Snapshot옵션으로 처리
ALTER DATABASE [DB명]
SET READ_COMMITTED_SNAPSHOT ON

=> 1번이 DB에 Snapshot옵션을 설정하는 명령어입니다. 2번 명령어는 SQL Server의 IsolationLevel ReadCommitted기본값이므로 해당 옵션을 사용할 때 Snapshot으로 처리하기 위한 명령어입니다. (확실한 것은 아니지만 2번으로 명령어를 사용하여 ReadCommitted로 테스트할 때와 1번 명령어만 사용하여 Snpashot옵션을 주어 처리할 때의 결과가 다른 적이 있어서 2번 명령어는 사용하지 않고 명시적으로 Snapshot옵션을 주는 것이 바람직해보입니다.)

그럼 Shapshot옵션을 주면 트랜잭션 사이의 격리성을 어떻게 관리하게 되는 것일까요? 트랜잭션이 시작되면 별도 DB(temp)에 트랜잭션 시퀀스 번호를 관리하여 트랜잭션 사이에 경합이 발생하면 경합이 발생한 트랜잭션 중 하나를 롤백(낙관적 동시성 모델)시켜서 데이터의 무결성을 보장하고 DeadLock을 방지해줍니다. 자세한 내용은 아래 MSDN을 참고하시면 됩니다. 여기서 중요한 것은 경합이 발생할 때 트랜잭션을 취소시키기 때문에 실패하는 트랜잭션이 발생하지만 동시성(성능)은 높은 옵션이라고 말할 수 있습니다. 다만 동시성이 높아지면 높아질수록 성공률은 낮아지게 되는 문제점이 있습니다.

https://msdn.microsoft.com/ko-kr/library/tcbchxcb(v=vs.110).aspx

그럼 정리를 해보면 트랜잭션 IsolationLevel중 성능을 고려하면 아래 2가지 중 하나를 선택할 수 밖에 없습니다.
1. ReadUncommitted => 단점은 더티 데이터로 인하여 데이터 무결성이 깨질 수 있습니다.
2. Snapshot => 동시성이 높아지면 성공률이 낮아집니다.

기본적으로는 ReadUncommitted를 사용하고 복잡한 트랜잭션에 대한 Lock을 관리하여 데이터의 무결성을 유지해야 합니다. Snapshot옵션은 동시성(DB)이 그렇게 높지 않은 시스템에서 안전하게 운영을 하고 싶을 경우에 사용하면 좋지 않을까 싶습니다.

DB Lock의 종류는 아래와 같습니다.
1. X-Lock(Exclusive, 배타) : Insert, Update, Delete와 같이 데이터 수정하는 작업을 말하며 같은 리소스에 대해서 동시에 실행될 수 없습니다. 즉 하나의 트랜잭션에서 X-Lock으로 잠금을 설정하면 다른 트랜잭션에서는 어떠한 잠금도 설정할 수 없습니다.
2. S-Lock(Shared, 공유) : 일반적으로 Select로 대표되며 데이터 읽기 작업을 합니다. 하나의 트랜잭션에서 S-Lock 잠금을 설정하면 다른 트랜잭션에서도 U-Lock, S-Lock 잠금이 가능합니다.
3. U-Lock(Update, 업데이트) : 설명을 짭게 하기가 힘들지만 U-Lock 잠금이 걸려 있어도 S-Lock만은 걸 수 있다 정도로 알면 될것 같습니다.
(이외에도 I-Lock, Schema, Bulk Update, Key-Range가 있지만 너무 많은 정보는 이해하는데 장애물이 되므로 패스하는 것이 좋습니다.)

일단은 이정도만 대략적으로 이해한 상태에서 간단한 예제를 통해서 DeadLock 상황을 만들어보고 원인과 해결 방법을 통해서 이해력을 높이면 충분히 DB도 아는 괜찮은 개발자라고 할 수 있을 것 같습니다. 저는 이미 괜찮은 개발자! ㅋㅋㅋ

참고 : Lock과 관련되어 상세하게 정리되어 있는 사이트입니다.
http://www.dbguide.net/db.db?cmd=view&boardUid=148215&boardConfigUid=9&categoryUid=216&boardIdx=138&boardStep=1


Posted by resisa
,

또?! 오랜만입니다 ㅎㅎ 지인이 애드센스 등록을 하셔서 저도 시도를 해보았더니 콘텐츠 부족으로 실패하여 이렇게 새로운 글을 쓰고 있습니다.

어떤 내용을 쓸까 고민을 하다가 블로그를 정리하면서 예전에 썼던 글 중에 그 당시 궁금했던 내용에 대한 답변을 해보고자 합니다.
(벌써 7년이 지났다니 시간이 참 빠릅니다;;)

TransactionScope 그리고 Stored Procedure (2010. 07. 25)
출처: http://resisa.tistory.com/110 [심플하게~]

(중략)...

1. 한 가지 의문점은 SP안에서는 트랜잭션의 Scope 범위(TransactionScopeOption) 지정하는 부분은 없어보이지만 IsolationLevel 대해서는 지정을   있습니다또한 TransactionScope에서도 IsolationLevel 대해서 지정할  있습니다그래서 IsolationLevel 따른 LTM SQLTransaction 관계가 어떻게 될지도 살짝 궁금해집니다. 하지만 실질적으로 이러한 것을 프로젝트에서 정확하게 알고 사용하는 개발자가 얼마나 될지도 의문이며 IsolationLevel 트랜잭션 사이의 잠금(lock) 대한 것이기 때문에 2. 성능보다는 안전성을 중요시하는 우리 나라의 대부분의 프로젝트에서는 ReadCommitted 사용하는 것이 개발자의 정신건강에 이로워 보입니다. ^^

예전에 썼던 글에 은근히 잘못된 내용이 있습니다;;  이유는 이해부족 입니다;;
저는 새로운 기술을 습득하는 과정에서 '정의'를 자신의 방식으로 표현하고 증명하는 과정을 반복합니다. '정의'와 '증명'을 잘못하면 위와 같이 잘못된 글이 나오기도 하지만 경험이 붙으면 증명하는 시간이 짧아지고 단순 실수도 줄어 이해하는 시간이 짧아지고 자신의 표현 방식으로 인하여 오랫동안 기억되는 장점이 있는 방식이라고 생각됩니다. 그럼 잘못된 부분부터 수정해보겠습니다.

=> 1번에서는 개념에 대해서 잘못 설명하고 있습니다. TransactionScopeOption은 TransactionScope클래스의 파라미터로 DB(SP)에는 없는 속성입니다.

=> 2번 대부분의 프로젝트에서 ReadCommitted로 설정할 것이라는 이유는 MSSQL의 기본 IsolationLevel이 ReadCommitted이기 때문입니다. 이 당시에는 IsolationLevel에 대한 이론적인 것만 알고 있었기 때문에 이런 결론을 냈었는데 업그레이드된?! 지금은 대부분의 프로젝트에서는 ReadUncommitted를 사용할 것이라고 생각합니다. 왜냐하면 ReadCommitted로 설정을 하면 DeadLock이 발생하는 구간이 많이 있을 수 있습니다. 그렇기 때문에 직접적으로 ReadUncommitted를 지정하지 않았더라도 이와 동일한 WITH(NOLOCK) 힌트를 많이 사용했을 것이라고 추측됩니다.

그럼 본론으로 들어가서 노란색 부분에 대한 이야기를 풀어보겠습니다.

먼저 트랜잭션을 아래와 같이 2가지 형태로 구분합니다.
1. 로컬 트랜잭션(SQL Transaction)
2. 분산 트랜잭션(DTC Transaction)

1. 로컬 트랜잭션을 세부화 하면 아래와 같이 나눌 수 있습니다.
1.1. TM(Transaction Manager)이 관여하는 SQL Transaction
1.2 TM이 관여하지 않는 SQL Transaction

개념적인 측면에서는 TM의 관여 여부로 나눌 수 있으며 개발적인 측면에서는 프로그램에서 트랜잭션을 관리하는지 DB에서 즉 SP에서 트랜잭션을 관리 여부로 나눌 수 있습니다.

다시 노란색 부분을 보면 SP에서 트랜잭션을 관리하는 부분들이 프로그램에서 트랜잭션을 관리하는 것과 합쳐졌을 경우에 트랜잭션 관리(Connection, IsolationLevel)에 대한 궁금증이였습니다.

프로그램에서 트랜잭션 관리를 하는 방법은 아래와 같습니다.
1. ADO.NET(System.Data.SqlClient.SqlConnection)를 사용하여 직접 BeginTransaction()를 호출하여 사용
2. System.Transactions.TransactionScope를 사용

트랜잭션을 관리하는 1번, 2번 방법의 공통점은 프로그램에서 트랜잭션을 관리하기 때문에 TM이 트랜잭션을  관리한다는 점입니다. 차이점이 바로 이 글의 핵심이 되는 답변으로 IsolationLevel과 관련되어 있습니다. TransactionScope에 IsolationLevel을 지정하여 사용할 때 SP의 IsolationLevel을 변경할 수 없다는 것입니다. IsolationLevel에 대한 위의 네임스페이스를 살펴보면 아래와 같습니다.

1. System.Data.SqlClient.SqlConnection - System.Data.IsolationLevel
2. System.Transactions.TransactionScope - System.Transactions.IsolationLevel

조심스럽게 예측해보면 TransactionScope의 트랜잭션은 DB 트랜잭션이라기보다는 프로그램에서 발생하는 트랜잭션(여기서는 DB 트랜잭션을 포함하는 상위의 트랜잭션의 개념으로 이해)이라고 할 수 있고 IsolationLevel은 트랜잭션 사이의 격리성이기 때문에 프로그램에서 발생하는 트랜잭션에 대한 격리성이지 DB 트랜잭션에 대한 격리성이 아니기 때문에 DB(SP) IsolationLevel을 변경할 수 없는 것입니다.

결론을 맺으면 프로그램에서 DB(SP) 트랜잭션 관리(IsolationLevel)를 하고 싶으면 SqlConnection을 사용하여 직접 트랜잭션을 발생(BeginTransaction())시켜야하며 TransactionScope을 통해서는 DB(SP) IsolationLevel까지는 관리할 수 없다는 것입니다.

Posted by resisa
,