'즐거운자료' 카테고리의 다른 글

롯데이라 산리오 인형세트 헬로키티 구매  (0) 2018.10.20
개발자로 살아남기  (0) 2011.01.07

2000년경 개발 첨 시작할때 보고 크게 감명 받았던 자료



'즐거운자료' 카테고리의 다른 글

롯데이라 산리오 인형세트 헬로키티 구매  (0) 2018.10.20
SI에 대한 통찰  (0) 2011.01.07

[sts@test01 ar]$ ls
bslib.c  bslib.h

[sts@test01 ar]$ gcc -c bslib.c

[sts@test01 ar]$ ls
bslib.c  bslib.h  bslib.o

[sts@test01 ar]$ ar r libbs.a bslib.o
ar: creating libbs.a

[sts@test01 ar]$ ar t libbs.a
bslib.o


* Makefile 에 아래옵션 추가

LIBS=-L경로 -lbs


-----------------------------------------------------------------------------------------------------------------
아래글의 원문  : http://kldp.org/node/1087

제가 무지하게 초보인디...어제..제가 공부한게 있길래..한번씁니다..ㅡㅡV

/usr/lib 에 보면 라이브러리들이 무지하게 많지요

정적라이브러리 .a 와 동적라이브러리 .so 가 있습니다.

이들은 ar 명령어로 내용을 확인할수 있는데..

우선 습관적으로 man ar 을 쳐보십시요..^^*

거기에 ar(archive) 은 묶음파일을 만들고 조작하고 추출한다라고
써있습니다.

음...우선 구경하나 하면..

$> ar t libc.a | less

이걸로 해당 .o 파일들이 묶어져있음을 알수있습니다.

라이브러리를 만들려면 우선 오브젝트파일이 필요함을 알수있지요

$> gcc -c test_lib.c
이렇게 하면 test_lib.o 파일이 만들어 집니다.

ar 의 r 옵션은 오브젝트를 추가하는거구여 s 옵션은 인덱스를 만든답니다.

$> ar rs libc.a test_lib.o

이렇게 하면 C 표준 라이브러리파일에 집어넣어주는게 됩니다.
오브젝트 파일을 한꺼번에 쭈~욱 쓰셔서 집어넣으셔두 됩니다.

아 ~ 물론 r 옵션으로 libc.a 말구 새로운걸 만드셔두 됩니다.

$> ar rs libhuk.a test_lib.o
이정도로 하죠

그러면 libhuk.a 가 생깁니다.

gcc -o huk_test_lib huk_test.c -lhuk -L.

이정도로 하시면 됩니다.

-lhuk 은 libhuk.a 를 사용하겠다는 의미이고
-L. 은 현재디렉에서 찼겠다는 겁니다. (이거 안해주면 /usr/lib 에서 libhuk.a 를 찾습니다.)

다시 라이브러리를 고치시구 싶으시면 새로 컴파일 하시구

ar rs 로 라이브러리를 갱신 시키시면 됩니다.

대충 이정도 입니다.

공부를 제대로 했나 모르겟군요...ㅡ.ㅡ;;
(나이먹구 책보구 독학하려니 힘들군요...아~ 전 생업은 있고 프밍을 취미인지라..)

나머지 부분은 man 페이지를 한번 보시는게...^^*


'STS' 카테고리의 다른 글

리눅스) umount : device is busy  (0) 2012.02.07
리눅스) 읽기전용 파일 시스템  (0) 2012.02.07


출처 : http://www.linxus.co.kr/post/43376

exec sp_lock

잠금 관련 정보를 보고합니다.

열 이름 데이터 형식 설명
spid smallint SQL Server 프로세스 ID 번호입니다.
dbid smallint 잠금을 요청하는 데이터베이스 ID 번호입니다.
ObjId int 잠금을 요청하는 개체의 개체 ID 번호입니다.
IndId smallint 인덱스 ID번호입니다.
Type nchar(4) 잠금 유형입니다.

DB = 데이터베이스
FIL = 파일
IDX = 인덱스
PG = 페이지
KEY = 키
TAB = 테이블
EXT = 익스텐트
RID = 행 식별자

Resource nchar(16) syslockinfo.restext의 값에 해당되는 잠금 리소스입니다.
Mode nvarchar(8) 잠금 요청자의 잠금 모드입니다. 이 잠금 모드는 허가된 모드, 변환 모드 또는 대기 모드를 표시합니다.
Status int 잠금 요청 상태입니다.

허가
대기
변환


exec sp_who {sp_id}

현재 Microsoft? SQL Server™ 사용자 및 프로세스에 관한 정보를 제공합니다. 유휴 상태가 아닌 프로세스만 반환하려면 반환되는 정보를 필터링하면 됩니다.

데이터 형식 설명
spid smallint 시스템 프로세스 ID입니다.
ecid smallint 특정 SPID와 관련된 주어진 스레드의 실행 컨텍스트 ID입니다.

ECID = {0, 1, 2, 3, ...n}이며, 0은 항상 주 또는 부모 스레드를 의미하며 {1, 2, 3, ...n}는 하위 스레드를 의미합니다.

status nchar(30) 프로세스 상태입니다.
loginame nchar(128) 특정 프로세스와 관련된 로그인 이름입니다.
hostname nchar(128) 각 프로세스의 호스트 또는 컴퓨터 이름입니다.
blk char(5) 프로세스를 차단하는 데 필요한 시스템 프로세스 ID입니다. 단, 존재하는 경우에 한합니다. 없는 경우, 이 열은 0이 됩니다.

고아 분산 트랜잭션이 주어진 spid 와 관련된 트랜잭션을 차단하면 이 열은 고아 트랜잭션을 차단하기 위해 '-2'를 반환합니다.

dbname nchar(128) 프로세스가 사용하는 데이터베이스입니다.
cmd nchar(16) Transact-SQL 문, SQL Server 내부 엔진 프로세스 등, 프로세스 실행에 필요한 SQL Server 명령입니다.


DBCC INPUTBUFFER( sp_id )

클라이언트에서 Microsoft? SQL Server™에 최종적으로 보낸 명령문을 표시합니다.

DBCC INPUTBUFFER는 다음과 같은 열이 있는 행 집합을 반환합니다.

열 이름 데이터 형식 설명
EventType nvarchar(30) 이벤트 유형(예: RPC, Language, NO Event)
Parameters Int 0 = 텍스트
1- n = 매개 변수
EventInfo nvarchar(255) RPC EventType의 경우, EventInfo에 프로시저 이름만 포함됩니다. Language 또는 No Event EventType의 경우, 이벤트의 첫 255자만 표시됩니다.

 


 

다음 내용은 MSDN에서 블록킹 해결하는 방법입니다.
 
블로킹은 잠금(Lock)을 기반으로 한 동시성을 지원하는 관계형 데이터베이스 관리 시스템(RDBMS)의 피할 수 없는 특성입니다. SQL Server에서 블로킹은 한 SPID가 특정 리소스에 대해 잠금(Lock)을 걸었는데 둘째 SPID가 동일한 리소스에 대해 충돌하는 잠금(Lock) 유형을 확보하려고 할 때 발생합니다. 일반적으로, 첫째 SPID가 리소스를 잠그는 시간 프레임은 매우 작습니다. 잠금(Lock)을 풀면 두번 째 연결은 자유롭게 리소스에서 자체 잠금(Lock)을 걸고 계속 처리할 수 있습니다. 이는 정상적인 동작이며 하루종일 시스템 성능에 인식할 수 있을만한 영향 없이 여러 번 일어날 수 있습니다.

쿼리의 기간 및 트랜잭션 컨텍스트는 잠금(Lock)이 걸리는 시간을 나타내고 그 결과 다른 쿼리에 영향을 줄 수 있습니다. 쿼리가 트랜잭션에서 실행되지 않으면(그리고 잠금 참고(Lock HInts)가 사용되지 않으면) SELECT 문에 대한 잠금(Lock)은 쿼리 시간 대신 실제로 읽힐 때 리소스에만 걸립니다. INSERT, UPDATE 및 DELETE 문에 대해 잠금(Lock)은 데이터 일관성 및 필요한 경우 쿼리 롤백을 위해 쿼리의 시간 동안 걸립니다.

트랜잭션에서 실행되는 쿼리에 대해 잠금(Lock)이 걸린 시간은 쿼리 유형, 트랜잭션 격리 수준(Isolation Level) 그리고 쿼리에서의 잠금 참고(Lock Hints) 사용 여부에 의해 결정됩니다. 잠금(Lock), 잠금 참고(Lock Hints) 참고 및 트랜잭션 격리 수준(Isolation Level)에 대한 설명은 SQL Server 온라인 설명서에 있는 다음 항목을 참조하십시오.
"Understanding Locking in SQL Server"
"Locking Architecture"
"Lock Compatibility"
"Locking Hints"
"Changing Default Locking Behavior in Oracle and SQL Server"
잠금(Lock) 및 블로킹이 시스템 성능을 저하시키는 지점까지 증가하는 경우 이는 일반적으로 다음과 같은 이유 중 하나에 의해서 입니다.

SPID는 잠금(Lock)을 풀기 전에 확장된 시간 동안 리소스 집합에 대해 잠금(Lock)을 겁니다. 이러한 블로킹 유형은 시간이 지나면서 스스로 해결될 수 있지만 성능이 저하될 수 있습니다.
SPID는 리소스 집합에 대해 잠금(Lock)을 걸고 이를 절대로 풀지 않습니다. 이러한 블로킹 유형은 자체적으로 해결되지 않으며 영향을 받은 리소스에 무한정으로 액세스하지 못하게 합니다.
위의 첫 시나리오에서 블로킹 문제는 SPID가 잠금(Lock)을 풀면 자체적으로 해결됩니다. 그러나 서로 다른 SPID는 시간이 지남에 따라 이동 대상을 만들면서 여러 리소스를 블로킹할 수 있으므로 상황은 매우 달라집니다. 이러한 이유로 SQL Server Enterprise Manager(엔터프라이즈 관리자) 또는 개별 SQL 쿼리를 사용하여 상황을 해결하기 힘듭니다. 두 번째 상황의 결과로 진단하기 쉬운 일관성 있는 상태가 나옵니다.

블로킹 정보 수집

블로킹 문제 해결의 어려움을 해결하기 위해 데이터베이스 관리자는 지속적으로 SQL Server에서의 잠금(Lock) 및 블로킹 상태를 모니터하는 SQL 스크립트를 사용할 수 있습니다. 이러한 스크립트는 시간에 따른 특정 인스턴스의 스냅샷을 제공함으로 문제의 전체적인 그림을 만들어냅니다. SQL 스크립트로 블로킹을 모니터하는 방법에 대한 설명은 Microsoft 기술 자료에 있는 다음 문서를 참조하십시오. 본 문서에 있는 스크립트는 아래의 작업을 수행합니다. 가능하면 Enterprise Manager(엔터프라이즈 관리자) 또는 특정 SQL 쿼리에서 이 정보를 얻는 방법이 제공됩니다.
1. 블로킹 체인 헤드에서 SPID를 식별합니다.
위의 문서에 나온 스크립트를 사용하는 것 외에 다음과 같이 SQL Enterprise Manager(엔터프라이즈 관리자)를 사용하여 블로킹 체인의 헤드를 식별할 수도 있습니다.

a. 서버 그룹을 확장한 다음 서버를 확장합니다.
b. Management를 확장한 다음 Current Activity를 확장합니다.
c. Locks / Process ID를 확장합니다. 블로킹 정보와 함께 SPID는 세부 정보 창에 표시됩니다. 다른 것을 블로킹하는 SPID는 "(Blocking)"으로 나타납니다.
그러나 일부 tempdb 블로킹 문제는 사용자가 임시 테이블 작업을 사용하는 쿼리를 실행하지 못하도록 할 수 있으므로 때때로 Enterprise Manager(엔터프라이즈 관리자) 대신 쿼리를 사용해야 할 때가 있습니다. 직접 쿼리를 사용하면 이 문제를 피하는데 필요한 컨트롤을 제공합니다.
2. 블로킹 SPID가 실행 중인 쿼리를 찾습니다.
스크립트 메서드는 다음 쿼리를 사용하여 특정 SPID에서 실행된 명령을 결정합니다.

DBCC INPUTBUFFER (<spid>)
또는 다음과 같이 SQL Enterprise Manager(엔터프라이즈 관리자)를 사용할 수 있습니다.

a. 서버 그룹을 확장한 다음 서버를 확장합니다.
b. Management를 확장한 다음 Current Activity를 확장합니다.
c. Process Info를 누릅니다. SPID는 세부 사항 창에 표시됩니다.
d. 블로킹 SPID를 두 번 눌러 SPID가 실행한 최근 Transact-SQL 명령 일괄 처리를 봅니다.
3. 블로킹 SPID가 걸고 있는 중인 블로킹 유형을 찾습니다.
sp_loc 시스템 저장 프로시저(Stored Procedure)를 실행하여 이 정보를 결정할 수 있습니다. 또는 다음과 같이 Enterprise Manager(엔터프라이즈 관리자)를 사용할 수 있습니다.

a. 서버 그룹을 확장한 다음 서버를 확장합니다.
b. Management를 확장한 다음 Current Activity를 확장합니다.
c. Locks / Process ID를 확장합니다. 걸고 있는 잠금(Lock)에 대한 정보와 함께 SPID는 세부 사항 창에 표시됩니다.
4. 블로킹 SPID의 트랜잭션 중첩 수준(Nesting Level) 및 프로세스 상태를 찾습니다.
SPID의 트랜잭션 중첩 수준(Nesting Level)은 @@TRANCOUNT 전역 변수에서 사용할 수 있습니다. 그러나 다음과 같이 sysprocesses 테이블을 쿼리하여 SPID 외부에서 결정될 수 있습니다.

SELECT open_tran FROM SYSPROCESSES WHERE SPID=<blocking SPID number>
go
반환된 값은 SPID의 @@TRANCOUNT 값입니다. 이는 블로킹 SPID의 트랜잭션 중첩 수준(Nesting Level)을 보여 주는데, 이는 잠금(Lock)을 건 이유를 설명할 수 있습니다. 예를 들어, 값이 0보다 크면 SPID는 트랜잭션 중간에 있습니다(트랜잭션 격리 수준에 따라 확보한 특정 잠금(Lock)을 걸 것으로 기대하는 경우).

DBCC OPENTRAN 데이터베이스_이름을 사용하여 데이터베이스에 장기 개방 트랜잭션이 있는지 확인할 수도 있습니다.

SQL Server Profiler(프로필러) 추적(Trace) 정보 수집

위의 정보 외에 서버에서 이루어진 작업의 Profiler(프로필러) 추적(Trace)을 캡처하여 SQL Server에서의 블로킹 문제를 철저히 확인해야 합니다. SPID가 한 트랜잭션에서 여러 문을 실행하면 마지막 문만 DBCC INPUTBUFFER 출력에 나타납니다. 그러나 이전 명령 중 하나가 아직도 잠금(Lock)이 걸려 있는 원인이 될 수 있습니다. Profiler(프로필러) 추적(Trace)을 사용하면 현재 트랜잭션에서 SPID에 의해 실행된 모든 명령을 볼 수 있습니다. 다음 단계는 SQL Server Profiler(프로필러)를 설정하여 추적(Trace)을 캡처할 수 있도록 합니다.
1. SQL Server Profiler(프로필러)를 엽니다.
2. Tools 메뉴에서 Options를 누릅니다.
3. All Event ClassesAll Data Columns 옵션을 선택해야 합니다.
4. OK를 누릅니다.
5. File 메뉴에서 New를 가리키고 Trace를 누릅니다.
6. General 탭에서 데이터를 캡처할 추적(Trace) 이름과 파일을 지정합니다.
7. Events 탭에서 다음과 같은 이벤트 유형을 추적(Trace)에 추가합니다.

Error and Warning Exception 이 이벤트는 예외가 발생했음을 나타냅니다.
심각도가 25 이하인 예외는 오류가 SQL Server에서 클라이언트로 반환되었음을 나타냅니다. 심각도가 25인 예외는 내부 SQL Server 예외이며 아래의 설명대로 필터 아웃되어야 합니다.
Misc. Attention 이 이벤트는 주의 신호가 발생하였음을 나타냅니다. 주의 신호의 일반 원인은 클라이언트 취소 또는 쿼리 시간 제한입니다.
Sessions Connect 이 이벤트는 새 연결이 만들어졌음을 나타냅니다.
Sessions Disconnect 이 이벤트는 클라이언트 연결이 끊어졌음을 나타냅니다.
Sessions Existing Connection 이 이벤트는 SQL Profiler(프로필러) 추적(Trace)이 시작되는 시점에서 연결이 이루어졌음을 나타냅니다.
TSQL RPC:Starting 이 이벤트는 원격 프로시저 호출(RPC)의 실행이 시작되었음을 나타냅니다.
TSQL SQL:BatchStarting 이 이벤트는 Transact-SQL 일괄 처리의 실행이 시작되었음을 나타냅니다.
Stored Procedures SP: StmtStarting 이 이벤트는 저장 프로시저(Stored Procedure) 내에 있는 문이 실행을 시작하고 있음을 나타냅니다. 저장 프로시저(Stored Procedure) 이름은 해당 이벤트에 대한 텍스트의 시작 부분에 나타납니나.

이 외에도, 자세한 정보를 위해 다음 이벤트를 포함시킬 수 있습니다. 대량 생산 환경에서 실행하는 경우 위의 이벤트는 블로킹 문제를 해결하기에 충분하므로 이러한 이벤트 중 하나만 사용할 수 있습니다. 아래의 추가 이벤트를 포함하면 문제의 근원을 좀더 쉽게 빨리 결정할 수 있지만 시스템의 로드가 추가되고 추적 출력(Trace Output) 크기가 늘어납니다.

Misc. Execution Plan 이 이벤트는 실행된 Transact-SQL 문의 계획(Plan) 트리를 보여줍니다.
Transactions DTCTransaction 이 이벤트는 두 개 이상의 데이터베이스 또는 서버 간의 MS DTC(Microsoft Distributed Transaction Coordinator) 트랜잭션을 추적합니다.
Transactions SQLTransaction 이 이벤트는 SQL BEGIN, SAVE, COMMIT 및 ROLLBACK TRANSACTION 문을 추적(Trace)합니다.
TSQL RPC:Completed 이 이벤트는 원격 프로시저 호출(RPC)의 실행이 완료되었음을 나타냅니다.
TSQL SQL:BatchCompleted 이 이벤트는 Transact-SQL 일괄 처리의 실행이 완료되었음을 나타냅니다.
Stored Procedures SP: StmtCompleted 이 이벤트는 저장 프로시저(Stored Procedure) 내에 있는 문이 실행을 완료했음을 나타냅니다.
8. Data Columns 탭에 다음 열(Column)이 있는지 확인하십시오. Start Time, End Time, Connection ID, SPID, Event Class, Text, Integer Data, Binary Data, Application Name, NT User Name 및 SQL User Name. 위의 두번째 테이블에서 추가 이벤트를 포함하면 Duration, CPU, Reads 및 Writes 데이터 열(Column) 또한 포함시키십시오.
9. Filters 탭에서 SQL Server 내부 예외를 제외합니다. Trace Event Criteria 상자에서 Severity를 선택하고 Maximum 상자에 24를 입력합니다. 그런 다음 OK를 누릅니다.

SQL Server에서 클라이언트로 보내진 오류를 모니터하는데 대한 자세한 내용은 Microsoft 기술 자료에 있는 다음 문서를 참조하십시오.
199037 INF: Trapping Error Messages Sent to Clients from a SQL Server
Profiler(프로필러) 사용에 대한 내용은 SQL Server 온라인 설명서를 참조하십시오.

일반 블로킹 시나리오 식별 및 해결

위의 정보를 확인하여 대부분의 블로킹 문제의 원인을 파악할 수 있습니다. 본 문서의 나머지는 이 정보를 사용하여 일부 일반 블로킹 시나리오를 식별하고 해결하는 방법에 대한 설명입니다. 이 설명에서는 문서 Q251004(앞에서 참조됨)에 있는 블로킹 스크립트를 사용하여 블로킹 SPID에 대한 정보를 캡처하고 위에서 설명한 이벤트를 가지고 Profiler(프로필러) 추적(Trace)을 만들었다고 가정합니다.

블로킹 스크립트 출력 보기

sysprocesses 출력을 확인하여 블로킹 체인의 헤드를 파악합니다.
블로킹 스크립트에 대해 고속 모드(Fast Mode)를 지정하지 않은 경우에는 스크립트 출력에는 다른 것을 블로킹하는 SPID를 나열하는 제목이 "SPIDs at head of blocking chains"인 부분이 있습니다.

   SPIDs at the head of blocking chains
   spid
   ------
   9
   10
고속 옵션(Fast Option)을 지정하면 계속 sysprocesses 출력을 보고 블로킹 헤드를 파악할 수 있습니다. 다음은 간결한 sysprocesses 출력입니다.

   spid   status                         blocked
    9     sleeping                       0
   10     sleeping                       0
   11     sleeping                       13
   12     sleeping                       10
   13     sleeping                       9
   14     sleeping                       12
이러한 경우 SPID 9와 10의 blocked 열(Column)에 0이 있음을 볼 수 있습니다. 이는 둘 다 블로킹되지 않았음을 의미하지만 기타 SPID에 대해 blocked 열(Column)에 나타납니다. 이는 SPID 9와 10이 별도의 블로킹 체인의 헤드에 있음을 나타냅니다.
블로킹 체인 헤드에 있는 SPID에 대한 정보에 대해 sysprocesses 출력을 확인합니다.
다음 sysprocesses 필드를 평가하는 것은 중요합니다.

Status
이 열(Column)은 특정 SPID의 상태를 볼 수 있게 합니다. 일반적으로, sleeping 상태는 SPID가 실행을 완료하고 응용 프로그램에서 다른 쿼리 또는 일괄 처리를 제출할 때까지 기다리고 있음을 나타냅니다. runnable 상태는 SPID가 현재 쿼리를 처리하고 있음을 나타냅니다. 다음 표는 다양한 상태 값에 대한 간단한 설명을 제공합니다.

Background SPID가 백그라운드 작업을 수행 중입니다.
Sleeping SPID가 현재 실행하고 있지 않습니다. 이는 일반적으로 SPID가 응용 프로그램에서 명령을 기다리고 있음을 나타냅니다.
Runnable SPID가 현재 실행 중입니다.
Dormant SPID가 연결이 재설정된 일시 정지 상태에 있음을 나타냅니다.
Rollback SPID가 트랜잭션 롤백 중입니다.
Defwakeup SPID가 해제되고 있는 프로세스에 있는 리소스를 기다리고 있음을 나타냅니다. waitresource 필드는 문제의 리소스를 나타내야 합니다.
Spinloop 프로세스가 SMP 시스템에서 동시성 제어에 사용되는 spinlock을 확보하려고 하면서 기다리고 있습니다.
Open_tran
이 필드는 SPID의 트랜잭션 중첩 수준(Nesting Level)을 알려줍니다. 이 값이 0보다 크면 SPID는 열린 트랜잭션에 있고 트랜잭션의 문으로 확보한 잠금(Lock)을 걸고 있을 것입니다.
Lastwaittype, waittype 및 waittime
lastwaittype 필드는 SPID의 마지막 또는 현재 대기 유형(waittype)을 알려줍니다. 이 필드는 SQL Server에서 새 필드이며 waittype 필드(예약된 내부 이진 열(Binary Column)임)의 문자열 표현입니다. waittype이 0x0000이면 SPID는 현재 아무것도 기다리고 있지 않으며 lastwaittype 값은 SPID에 있던 마지막 waittype을 나타냅니다. waittype이 0이 아니면 lastwaittype 값은 SPID의 현재 waittype을 나타냅니다.

여러 lastwaittypewaittype 값에 대한 간단한 설명에 대해서는 Microsoft 기술 자료에 있는 다음 문서를 참조하십시오.
244455 INF: Definition of Sysprocesses Waittype and Lastwaittype Fields
waittime 값은 SPID가 진행 중인지를 파악할 때 유용할 수 있습니다. sysprocesses 테이블에 대한 쿼리에서 sysprocesses의 이전 쿼리의 waittime 값보다 작은 값을 waittime 열에 반환하면 이는 이전 잠금(Lock)이 확보되었다가 플렸으며 현재 새 잠금(Lock)을 기다리고 있음을 나타냅니다(waittime은 0이 아님을 가정). 이는 sysprocesses 출력 간에 waitresource를 비교하여 확인할 수 있습니다.
Waitresource
이 필드는 SPID가 기다리고 있는 리소스를 나타냅니다. 다음 표는 일반 waitresource 형식과 그 의미를 나열합니다.

테이블 DatabaseID:ObjectID TAB: 5:261575970
이 경우 데이터베이스 ID 5는 pubs 예제 데이터베이스이고 개체 ID 261575970은 titles테이블입니다.
페이지 DatabaseID:FileID:PageID PAG: 5:1:104
이 경우 데이터베이스 ID 5는 pubs이고 파일 ID 1은 기본 데이터 파일이며 페이지 104는 titles 테이블에 속하는 페이지입니다.
DatabaseID:ObjectID:IndexID(인덱스 키에 대한 해시(Hash) 값) 키: 5:261575970:1 (5d0164fb1eac)
이 경우 데이터베이스 ID 5는 pubs이고 개체 ID 261575970은 titles 테이블이며 인덱스 ID 1은 클러스터된 인덱스이고 해시(Hash) 값은 특정 행의 인덱스 키 값을 나타냅니다.
DatabaseID:FileID:PageID:Slot(row) RID: 5:1:104:3
이 경우 데이터베이스 ID 5는 pubs이고 파일 ID 1은 기본 데이터 파일이며 페이지 104는 titles 테이블에 속하는 페이지이고 슬롯 3은 페이지에서 행의 위치를 나타냅니다.
컴파일 DatabaseID:ObjectID TAB: 5:834102012 [[COMPILE]]
이 경우 데이터베이스 ID 5는 pubs이지만 개체 ID 834102012는 저장 프로시저(Stored Procedure)입니다. 이는 SPID가 저장 프로시저(Stored Procedure)에 대한 계획(Plan)이 컴파일되기를 기다리고 있음을 나타냅니다.
기타 열(Column)
나머지 sysprocesses 열(Column)은 문제의 근본에 대한 통찰력도 제공합니다. 그 유용성은 문제의 상황에 따라 달라집니다. 예를 들어, SPID에서 제출한 마지막 일괄 처리가 (last_batch)일 때 특정 네트워크 라이브러리(net_library)에서 특정 클라이언트(hostname)로부터만 발생하는지 파악할 수 있습니다. 모든 sysprocesses 열에 대한 간단한 설명에 대해서는 SQL Server 7.0 온라인 설명서에 있는 "sysprocesses(T-SQL)" 항목을 참조하십시오.

참고: SUID 열(Column)은 역방향 호환성을 위해서만 포함된 열(Column)에서 파생되므로 블로킹 스크립트 출력에 포함되지 않습니다. 이는 SQL Server에서 내부적으로 사용되지 않으며 이를 쿼리하면 성능이 저하될 수 있으므로(파생되었으므로) 포함되지 않습니다.
DBCC INPUTBUFFER 출력을 확인합니다.

블로킹 체인의 헤드에 있는 SPID에 대해 또는 0이 아닌 waittype을 가지고 블로킹 스크립트는 DBCC INPUTBUFFER를 실행하여 그 SPID에 대한 현재 쿼리를 결정합니다.

   DBCC INPUTBUFFER FOR SPID 9
   EventType      Parameters EventInfo
   -------------- ---------- --------------------------------------------
   Language Event 0          update titles set title = title
대부분의 경우 이 쿼리는 다른 사용자를 블로킹하는 잠금(Lock)을 걸 수 있습니다 . 그러나 트랜잭션에 SPID가 있으면 잠금(Lock)은 현재 쿼리가 아닌 이전에 실행된 쿼리에서 얻을 수 있습니다. 그러므로 inputbuffer만 아니라 SPID에 대해 Profiler(프로필러) 출력도 봐야 합니다.

참고: 블로킹 스크립트는 여러 단계로 구성되므로 SPID는 블로킹 체인의 헤드에 첫 섹션으로 나타날 수 있지만 DBCC INPUTBUFFER 쿼리가 실행될 때 이는 더 이상 블로킹되지 않고 INPUTBUFFER는 캡처되지 않습니다. 이는 그 SPID에 대해 블로킹이 스스로를 해결하고 있음을 나타내는데, 이는 문제가 될 수도 있고 그렇지 않을 수도 있습니다. 이 때 블로킹 스크립트의 고속(Fast) 버전을 사용하여 inputbuffer가 지워지기 전에 이를 캡처하거나(비록 보장할 수는 없어도) 그 시간 프레임에서 Profiler(프로필러) 데이터를 보고 SPID가 실행하는 쿼리를 결정할 수 있습니다.
DBCC PSS 출력을 확인합니다.
블로킹 체인의 헤드에 있는 모든 SPID에 대해 DBCC PSS 명령을 실행합니다. PSS는 프로세스 상태 구조로 특정 SPID의 상태에 대한 정보가 들어 있습니다. 블로킹 시나리오의 가장 큰 관심 필드는 ec_stat 필드입니다. 다음은 ec_stat 필드의 일반 값입니다.

0x2 프로세스가 종료되었습니다. 이는 SPID가 중단되었음을 나타냅니다.
0x40 프로세스는 쿼리 시간 제한 또는 취소에 의해 클라이언트로부터 주의 신호를 받았습니다. 이는 현재 활성 쿼리가 중단되고 있기는 하지만 0x2에서와 같이 전체 SPID는 아님을 나타냅니다.
0x80 프로세스가 여러 데이터베이스 트랜잭션에 관련되어 있습니다.
0x200 프로세스가 서버 간 원격 프로시저 호출(RPC)를 수행하고 있습니다.
0x400 사용자가 NOLOCK 참고(Hint)가 있는 쿼리를 수행하고 있습니다.
0x800 프로세스가 롤백 중이며 교착 상태 대상으로 선택될 수 없습니다.
0x2000 프로세스가 현재 중단되고 있습니다.


참고: 고속 모드(Fast Mode)에서 블로킹 스크립트는 DBCC PSS 명령을 실행하지 않습니다. 이 정보의 대부분은 Profiler(프로필러) 추적(Trace) 데이터를 보고 결정될 수 있습니다. 추적(Trace) 파일에서 ATTENTION 신호와 예외를 볼 수 있습니다. 예를 들어, 프로세스가 교착 상태에 있으면 교착 상태가 발생할 때 클라이언트에 반환되는 오류인 정수 데이터 열(Column) 값이 1205인 예외 이벤트를 볼 수 있습니다. ec_stat 값은 0이 아닐 때 좀더 자세히 살펴 보기 위한 신호로 유용하게 사용됩니다. SPID가 "정상" 상태에 있지 않으면 경고합니다. 위의 표와 Profiler(프로필러) 추적(Trace) 정보에서 이에 대한 원인과 그 진행 방법을 파악할 수 있습니다.

Profiler(프로필러) 데이터 보기

블로킹 문제 해결에서 Profiler(프로필러) 데이터를 효율적으로 보는 것이 매우 중요합니다. 가장 중요한 사실은 캡처한 모든 것을 볼 필요 없이 선택적으로 볼 수 있다는 것입니다. Profiler(프로필러)는 캡처된 데이터를 효과적으로 볼 수 있도록 기능을 제공합니다. Properties 대화 상자(File 메뉴에서 Properties 누름)에서 Profiler(프로필러)를 사용하여 데이터 열(Column) 또는 이벤트를 제거하고 데이터 열(Column)을 그룹화(정렬)하며 필터를 적용하여 표시되는 데이터를 제한할 수 있습니다. 전체 추적(Trace)을 검색하거나 Edit 메뉴에서 Find를 눌러 특정 값에 대해 특정 열(Column)만 검색할 수도 있습니다. Profiler(프로필러) 데이터를 SQL Server 테이블(File 메뉴에서 Save As를 가리킨 다음 Table을 누름)에 저장하고 이에 대해 SQL 쿼리를 실행할 수도 있습니다.

이전에 저장한 추적(Trace) 파일에만 필터링을 수행해야 합니다 . 활성화된 추적(Trace)에서 이러한 단계를 수행하면 추적(Trace)을 시작한 후에 캡처한 데이터는 손실될 우려가 있습니다. 먼저 활성화된 추적(Trace)을 파일 또는 테이블에 File 메뉴에서 Save As를 눌러 저장한 다음 진행하기 전에 이를 File 메뉴에서 Open을 눌러 다시 엽니다. 저장된 추적(Trace) 파일에서 작업할 때는 필터링이 필터된 데이터를 영구적으로 제거하지 않으며 단지 모든 데이터를 표시하지 않을 뿐입니다. 특히 중점을 두는 검색에 필요한 이벤트와 데이터 열(Column)을 추가하거나 삭제할 수 있습니다.

찾아야 할 내용:
현재 트랜잭션에서 블로킹 체인의 헤드에 있는 SPID에서 어떤 명령을 실행했습니까?
블로킹 체인의 헤드에 있는 특정 SPID의 추적(Trace) 데이터를 필터합니다(File 메뉴에서 Properties를 누른 다음 Filters 탭에서 SPID 값을 지정함). 그러면 다른 SPID를 블로킹하기 전에 실행한 명령을 확인할 수 있습니다. 트랜잭션 이벤트를 포함하면 트랜잭션이 언제 시작되었는지를 쉽게 식별할 수 있습니다. 그렇지 않으면 Text 열에서 BEGIN, SAVE, COMMIT 또는 ROLLBACK TRANSACTION 작업을 검색할 수 있습니다. sysprocesses 테이블에서 open_tran 값을 사용하여 모든 트랜잭션 이벤트를 잡아야 합니다. 실행된 명령과 트랜잭션 컨텍스트를 알면 SPID에서 잠금(Lock)을 걸고 있는 이유를 파악할 수 있습니다.

이벤트와 데이터 열(Column)을 제거할 수 있다는 것을 유의하십시오. 시작 및 완료 이벤트를 둘 다 보는 대신 하나를 선택하십시오. 블로킹 SPID가 저장 프로시저(Stored Procedure)가 아니면 SP:Starting 또는 SP:Completed 이벤트를 제거합니다. SQLBatchRPC 이벤트는 프로시저 호출을 보여 줍니다. SP 이벤트의 세부 사항 수준을 봐야 하는 경우에만 이를 보십시오.
블로킹 체인의 헤드에 있는 SPID의 쿼리 기간은 어느 정도입니까?
위의 완료된 이벤트를 포함시킬 경우 Duration 열은 쿼리 실행 시간을 보여 줍니다. 이를 사용하면 블로킹을 일으키는 장기간 실행되는 쿼리를 식별할 수 있습니다. 쿼리의 수행 속도가 느린 이유를 파악하려면 CPU, ReadWrites 열(Column)뿐만 아니라 Execution Plan 이벤트도 보십시오.

일반 블로킹 시나리오 분류

다음 표는 일반 증상과 그에 해당되는 원인을 나타냅니다. Scenario 열에 표시된 번호는 아래 문서의 "일반 블로킹 시나리오 및 해결책" 절에 있는 번호에 해당합니다. Waittype , Open_TranStatus 열(Column)은 sysprocesses 정보입니다. Resolves? 열(Column)은 블로킹이 자체적으로 해결되었는지 여부를 나타냅니다.

1 0이 아님 >= 0 runnable 예, 쿼리가 완료할 때입니다. Physical_IO, CPU 및/또는 Memusage 열(Column)은 시간이 지나면서 증가합니다. 쿼리의 기간은 완료될 때 높아집니다.
2 0x0000 >0 sleeping 아니오, 그러나 SPID는 중단됩니다. 이 SPID에 대해 Profiler(프로필러) 추적(Trace)에서 주의 신호를 볼 수 있는데 이는 쿼리 시간 제한 또는 취소가 발생하였음을 나타냅니다.
3 0x0000 >= 0 runnable 아니오. 클라이언트가 모든 행을 반입하거나 연결을 닫을 때까지 해결되지 않습니다. SPID는 중단될 수 있습니다. 그러나 중단하는데 30초 정도 걸립니다. open_tran = 0이고 SPID가 트랜잭션 격리 수준(Isolation Level)이 기본(READ COMMITTED)인 동안 잠금(Lock)을 걸고 있는데, 이것이 그 원인일 가능성이 있습니다.
4 다양화 >= 0 runnable 아니오. 클라이언트가 쿼리를 취소하거나 연결을 닫을 때까지 해결되지 않습니다. SPID는 중단될 수 있습니다. 그러나 중단하는데 30초 정도 걸립니다. 블로킹 체인의 헤드에 있는 SPID의 sysprocesseshostname 열(Column)은 블로킹 중인 SPID 중 하나와 같습니다.
5 0x0000 >0 rollback 예. 이 SPID의 Profiler(프로필러) 추적(Trace)에서 주의 신호를 볼 수 있는데, 이는 쿼리 시간 제한 또는 취소가 일어났거나 단순히 롤백 문이 실행되었음을 나타냅니다.
6 0x0000 >0 sleeping 결국, Windows NT가 세션이 더 이상 활성화되어 있지 않다고 확인하면 SQL Server 연결은 끊어집니다. sysprocesseslast_batch 값은 현재 시간보다 훨씬 앞섰습니다.

일반 블로킹 시나리오 및 해결책

아래에 나열된 시나리오에는 위의 표에 있는 특성이 있습니다. 이 절에서는 해당되는 경우 추가 세부 사항 및 해결 방법을 제공합니다.
1. 실행 시간이 긴 일반 실행 쿼리에 의한 블로킹

해결책:
이러한 유형의 블로킹 문제에 대한 해결책은 쿼리를 최저화하는 방법을 찾는 것입니다. 사실 이러한 유형의 블로킹 문제는 성능 문제일 수 있으므로 성능 문제의 관점에서 해결해야 합니다. 쿼리의 실행 시간이 오래 걸리는 문제를 해결하는 방법은 Microsoft 기술 자료에 있는 다음 문서를 참조하십시오.
243589 INF: Troubleshooting Slow-Running Queries on SQL Server 7.0
전반적인 응용 프로그램 성능 문제 해결에 대한 내용은 Microsoft 기술 자료에 있는 다음 문서를 참조하십시오.
224587 INF: Troubleshooting Application Performance with SQL Server
다른 사용자를 블로킹하는 실행 시간이 긴 쿼리가 있는데 이를 최적화할 수 없으면 이를 OLTP 환경에서 의사 결정 지원 시스템으로 이동하십시오.
2. 트랜잭션 중첩 수준(Nesting Level)의 추적(Trace)을 놓친 Sleeping 상태의 SPID에 의한 블로킹

이러한 블로킹 유형은 일시 정지(Sleeping)되었거나 명령을 기다리는(Waiting) SPID에서 식별될 수 있으나 트랜잭션 중첩 수준(Nesting Level)(@@TRANCOUNT, sysprocesses에서 open_tran)은 0보다 큽니다. 이는 응용 프로그램에서 쿼리 시간 제한을 겪거나 필요한 수만큼의 ROLLBACK 및/또는 COMMIT 문을 실행하지 않고 취소를 실행할 경우에도 발생합니다. SPID가 쿼리 시간을 제한하거나 취소를 받으면 이는 현재 쿼리 및 일괄 처리를 종료하지만 트랜잭션을 자동으로 롤백하거나 커밋하지 않습니다. SQL Server는 취소 중인 단일 쿼리로 인해 전체 트랜잭션을 단순히 롤백해야 한다고 간주할 수 없으므로 응용 프로그램은 이에 대한 책임이 있습니다. 쿼리 시간을 제한하거나 취소하는 것은 Profiler(프로필러) 추적(Trace)에서 SPID에 대해 ATTENTION 신호 이벤트로 나타납니다.

이를 표시하려면 Query Analyzer(쿼리 분석기)에서 다음과 같은 간단한 쿼리를 실행하십시오.

BEGIN TRAN
SELECT * FROM SYSOBJECTS S1, SYSOBJECTS S2

-- Issue this after canceling query
SELECT @@TRANCOUNT
ROLLBACK TRAN
쿼리가 실행 중인 동안 빨간색의 Cancel 단추를 누릅니다. 쿼리가 취소된 다음 SELECT @@TRANSCOUNT는 트랜잭션 중첩 수준(Nesting Level)이 하나임을 나타냅니다. 이것이 DELETE 또는 UPDATE 쿼리이거나 HOLDLOCK가 SELECT에 사용된 경우에는 얻은 잠금(Lock)이 계속 유지됩니다. 위의 쿼리에서조차 다른 쿼리를 얻고 트랜잭션 초기 잠금(Lock)을 건 경우에 위의 SELECT가 취소되어도 계속 유지됩니다.

해결책:

응용 프로그램은 제대로 트랜잭션 중첩 수준(Nesting Level)을 관리해야 하는데 그렇게 하지 않으면 이러한 방식으로 쿼리 취소 다음에 블로킹 문제를 일으킬 수 있습니다.
사실 이러한 유형의 블로킹 문제 또한 성능 문제일 수 있으므로 성능 문제의 관점에서 해결해야 합니다. 쿼리 실행 시간이 줄어들면 쿼리 시간 제한 또는 취소가 일어나지 않습니다. 시간 제한 또는 취소 시나리오가 발생할 때 응용 프로그램에서 이를 처리하는 것도 중요하지만 쿼리의 성능을 확인하여 이점을 얻을 수도 있습니다.

쿼리의 실행 시간이 오래 걸리는 문제를 해결하는 방법은 Microsoft 기술 자료에 있는 다음 문서를 참조하십시오.
243589 INF: Troubleshooting Slow-Running Queries on SQL Server 7.0
전반적인 응용 프로그램 성능 문제 해결에 대한 내용은 Microsoft 기술 자료에 있는 다음 문서를 참조하십시오.
224587 INF: Troubleshooting Application Performance with SQL Server
다른 사용자를 블로킹하는 실행 시간이 긴 쿼리가 있는데 이를 최적화할 수 없으면 이를 OLTP 환경에서 의사 결정 지원 시스템으로 이동하십시오.
3. 모든 결과 행을 완료로 반입하지 못한 해당 클라이언트 응용 프로그램의 SPID에 의한 블로킹

이 문제는 응용 프로그램을 제대로 설계하지 못해 일어납니다. 쿼리를 서버에 보낸 다음 모든 응용 프로그램은 모든 결과 행을 즉시 완료로 반입해야 합니다. 응용 프로그램에서 모든 결과 행을 반입하지 않으면 잠금(Lock)은 다른 사용자를 블로킹하면서 테이블에 남습니다. 투명하게 SQL 문을 서버에 제출하는 응용 프로그램을 사용하는 경우 응용 프로그램은 모든 결과 행을 반입해야 합니다. 그렇게 하지 않으면(그리고 이렇게 하도록 구성되어 있지 않으면) 블로킹 문제를 해결할 수 없습니다. 이러한 문제를 피하려면 제대로 작동하지 않는 응용 프로그램을 보고 또는 의사 결정 지원 데이터베이스로 제한할 수 있습니다.

해결책:

결과의 모든 행을 완료로 반입할 수 있도록 응용 프로그램을 다시 작성해야 합니다.
4. 분산 클라이언트/서버 교착 상태에 의한 블로킹

전형적인 교착 상태와는 달리 분산 교착 상태는 RDBMS 잠금(Lock) 관리자로 감지할 수 없습니다. 이는 교착 상태에 관련된 유일한 리소스는 SQL Server 잠금(Lock)이라는 사실 때문에 일어납니다. 교착 상태의 다른 측면은 클라이언트 응용 프로그램 수준에서 일어나는데, SQL Server는 이에 대해 제어할 수 없습니다. 다음은 이러한 문제가 발생할 수 있는 두 가지 예제 및 응용 프로그램에서 이를 피할 수 있는 방법입니다.

a. 단일 클라이언트 스레드와의 클라이언트/서버 분산 교착 상태
클라이언트에 열린 연결이 여러 개 있고 단일 실행 스레드가 있으면 다음과 같은 분산 교착 상태가 일어날 수 있습니다. 간단히 여기서 사용되는 "dbproc" 용어는 클라이언트 연결 구조를 의미합니다.

 SPID1------잠금(Lock)으로 ------->SPID2
  /\          블로킹됨        (클라이언트로 결과
  |                            기록 대기)
  |                                 |
  |                                 |                           서버측
  | ================================|==================================
  |     <-- 단일 스레드 -->         |                     클라이언트측
  |                                 \/
 dbproc1   <-------------------   dbproc2
 (다음 행                      (dbproc1에서 효과적으로 블로킹되어
  반입 대기)                    실행하기 위해 단일 스레드 실행 대기)
위의 경우 단일 클라이언트 응용 프로그램 스레드에는 열린 연결이 두 개 있습니다. 이는 비동기적으로 SQL 작업을 dbproc1에 제출합니다. 이는 진행하기 전에 반환하기 위해 호출을 기다리지 않음을 의미합니다. 그러면 응용 프로그램은 dbproc2에서 다른 SQL 작업을 제출하고 결과를 기다려 반환된 데이터를 처리합니다. 데이터가 되돌아 오기 시작하면(어떤 dbproc이 먼저 응답하든지 간에, 이를 dbproc1로 간주) 그 dbproc에 반환된 모든 데이터가 완료될 때까지 처리합니다. SPID1이 SPID2에서 걸고 있는 잠금(Lock)에서 블로킹될 때까지 두 쿼리가 서버에서 비동기로 실행되고 있으므로 dbproc1에서 결과가 반입됩니다. 이 때 dbproc1은 더 많은 데이터를 무한정 기다립니다. SPID2는 잠금(Lock)에서 블로킹되지만 데이터를 그 클라이언트인 dbproc2로 보내려고 합니다. 그러나 응용 프로그램 실행의 단일 스레드가 dbproc1에서 사용되므로 dbproc2는 응용 프로그램 계층의 dbproc1에서 효과적으로 블로킹됩니다. 리소스 중 SQL Server 리소스만 관련되어 있으므로 SQL Server가 감지하거나 해결할 수 없는 교착 상태가 발생합니다.
b. 각 연결마다 스레드가 있는 클라이언트/서버 분산 교착 상태

클라이언트의 각 연결마다 별도의 스레드가 있어도 이 분산 교착 상태의 변형이 다음에서와 같이 계속 발생합니다.

SPID1--------잠금(Lock)으로 -------->SPID2
  /\           블로킹됨        (네트 쓰기 시 대기)               서버측
  |                                 |
  |                                 |
  | INSERT                          |SELECT
  | ================================|==================================
  | <-- 각 dbproc 당 하나의  -->    |                      클라이언트측
  |           스레드                \/
 dbproc1   <-----데이터 행-----    dbproc2
 (삽입 시                      (dbproc1에서 블로킹되어 버퍼에서
  대기)                         행 읽는 작업 대기)
이 경우는 예제 A와 비슷합니다. 단, dbproc2 및 SPID2는 한번에 한 행씩 처리하고 동일한 테이블에서의 INSERT, UPDATE 또는 DELETE 문에 대해 각 행을 버퍼를 통해 dbproc1으로 전달할 의도를 가지고 SELECT 문을 실행하고 있습니다. 결국, SPID1(INSERT, UPDATE 또는 DELETE 수행)은 SPID2(SELECT 수행)에서 걸고 있는 잠금(Lock)으로 블로킹됩니다. SPID2는 클라이언트 dbproc2에 결과 행을 기록합니다. 그러면 Dbproc2는 버퍼에 있는 행을 dbproc1으로 전달하려고 하지만 dbproc1이 사용 중임을 알게 됩니다(이는 SPID2에 의해 블로킹된 SPID1에서 현재 INSERT를 완료하기를 기다리도록 블로킹됨) 이 때 dbproc2는 SPID(SPID1)가 SPID2에 의해 데이터베이스 수준에서 블로킹된 dbproc1에 의해 응용 프로그램 계층에서 블로킹됩니다. 리소스 중 SQL Server 리소스만 관련되어 있으므로 다시 한번 SQL Server가 감지하거나 해결할 수 없는 교착 상태가 발생합니다.
예제 A 및 B는 둘 다 응용 프로그램 개발자가 알고 있어야 하는 기본적인 문제입니다. 개발자는 이러한 경우를 제대로 처리할 수 있는 응용 프로그램을 작성해야 합니다.

해결책:

두 가지 확실한 해결책은 쿼리 시간 제한을 사용하거나 바운드 연결을 사용하는 것입니다.

쿼리 시간 제한
쿼리 시간 제한이 제공되었을 때 분산 교착 상태가 발생하면 시간 제한이 일어날 때 교착 상태가 중단됩니다. 쿼리 시간 제한 사용에 대한 자세한 내용은 DB-Library 또는 ODBC 설명서를 참조하십시오.
바운드 연결
이 기능을 통해 여러 개의 연결이 있는 클라이언트는 단일 트랜잭션 공간으로 바인드할 수 있으므로 연결은 서로 블로킹하지 않습니다. 자세한 내용은 SQL Server 7.0 온라인 설명서의 "Using Bound Connection" 항목을 참조하십시오.
5. "골든" 또는 롤백 상태에 있는 SPID에 의한 블로킹

중단되었거나 사용자 정의 트랜잭션 외부에서 취소된 데이터 수정 쿼리는 롤백됩니다. 이는 또한 클라이언트 컴퓨터가 다시 시작되고 해당 네트워크 세션 연결이 끊어지질 때 그 부작용으로서 발생합니다. 마찬가지로 교착 상태의 대상으로 선택된 쿼리는 롤백됩니다. 데이터 수정 쿼리는 종종 변경 사항이 초기에 적용되었을 때보다 더 빠르게 롤백될 수 없습니다. 예를 들어, DELETE, INSERT 또는 UPDATE 문이 몇 시간 동안 실행 중이었으면 롤백하는데 최소한 한 시간은 걸립니다. 변경 사항은 완전히 롤백되거나 데이터베이스에서의 트랜잭션 및 실제 무결성은 보완되므로 이는 예상했던 동작입니다. 이 동작은 반드시 발생해야 하므로 SQL Server는 SPID를 "골든" 또는 롤백(이를 중단하거나 교착 상태 대상으로 선택할 수 없음을 의미) 상태로 표시합니다. ROLLBACK 명령을 나타내는 sp_who의 출력을 관찰하여 이를 확인할 수 있습니다. sysprocessesStatus 열은 sp_who 출력 또는 SQL Enterprise Manager(엔터프라이즈 관리자) 현재 작업 화면에도 나타나는 ROLLBACK 상태를 나타냅니다. 이 정보를 볼 수 있는 가장 확실한 방법은 문제의 블로킹 SPID의 DBCC PSS를 조사하고 ec_stat 값을 관찰하는 것입니다. "골든" SPID는 ec_stat 값에 0x800를 가집니다. 기타 일반 ec_stat 값은 이 기사의 "DBCC PSS 출력을 확인합니다." 절에 나열되어 있습니다.

해결책:

SPID가 변경 사항을 롤백할 때까지 기다려야 합니다.

서버가 이 작업 중간에 종료되면 데이터베이스는 복구 모드에서 다시 시작되고 열린 모든 트랜잭션이 처리될 때까지 액세스할 수 없습니다. 시작 복구는 런타임 복구로서 트랜잭션마다 걸리는 것과 같은 시간이 걸리고 데이터베이스는 이 기간 동안 액세스될 수 없습니다. 그러므로, 서버를 강제로 종료하여 롤백 상태에 있는 SPID를 수정하면 성능이 저하됩니다.

이러한 상황을 피하려면 바쁜 시간에 OLTP 시스템에서 대규모 일괄 처리 INSERT, UPDATE 또는 DELETE 작업을 수행하지 마십시오. 가능하면 이러한 작업을 한가한 시간에 수행하도록 하십시오.
6. 고아가 된 연결에 의한 블로킹

클라이언트 응용 프로그램이 트랩되거나 클라이언트 워크스테이션이 다시 시작되면 서버에 대한 네트워크 세션은 몇몇 조건 하에서 즉시 취소되지 않을 수도 있습니다. 서버의 측면에서 클라이언트는 계속 존재하는 것으로 나타나고 얻은 모든 잠금(Lock)은 계속 유지되고 있는 것으로 나타납니다. 자세한 내용은 SQL Server 7.0 온라인 설명서에 있는 "Orphaned Connections" 항목을 참조하십시오.

해결책:

클라이언트 응용 프로그램이 해당 리소스를 제대로 정리하지 않고 연결이 끊어진 경우 KILL 명령을 사용하여 SPID를 종료할 수 있습니다. KILL 명령은 SPID 값을 입력으로 받습니다. 예를 들어, SPID 9를 중지하려면 다음 명령을 실행하면 됩니다.

KILL 9

참고: KILL 명령은 KILL 명령에 대한 검사 간의 간격으로 인해 완료하는데 30초 정도 걸립니다.

응용 프로그램의 블로킹 문제 관련

블로킹 문제가 발생했을 때 서버 측 조정 및 플랫폼 문제에 중점을 두는 경향이 있습니다. 그러나 이는 일반적으로 해결책을 제시하지는 않으며 클라이언트 응용 프로그램 및 제출하는 쿼리 검사에 기울일 수 있는 시간과 노력을 빼앗을 수 있습니다. 데이터베이스 호출에 대해 응용 프로그램에서 노출하는 가시성 수준에 관계없이 블로킹 문제에서는 응용 프로그램에서 제출한 정확한 SQL 문 및 쿼리 취소, 연결 관리, 모든 결과 행 반입 등에 관한 응용 프로그램의 동작이 정확한지 조사가 이루어져야 합니다. 개발 도구가 연결 관리, 쿼리 취소, 쿼리 시간 제한, 결과 반입 등에 대한 명시적인 제어를 허용하지 않은 경우 블로킹 문제를 해결할 수 없습니다. SQL Server, 특히 업무에 중요한 OLTP 환경에 대한 응용 프로그램 개발 도구를 선택하기 전에 이러한 가능성을 세밀하게 조사해야 합니다.

데이터베이스 및 응용 프로그램의 설계 및 구성 단계에서 이러한 상황을 매우 신중하게 검토해야 합니다. 특히, 리소스 사용, 격리 수준(Isolation Level) 및 트랜잭션 경로 길이를 각 쿼리마다 평가해야 합니다. 각 쿼리 및 트랜잭션은 가능한 한 라이트웨이트이어야 합니다. 적합한 연결 관리 규칙을 적용해야 합니다. 그렇게 하지 않으면 응용 프로그램은 사용자 수가 적을 때는 좋은 성능을 내는 것처럼 보이지만 사용자 수가 급증하면 성능이 확연하게 저하될 수 있습니다.

제대로 된 응용 프로그램 및 쿼리 설계를 통해 Microsoft SQL Server는 수천 명의 동시 사용자를 거의 블로킹하지 않고 단일 서버에서 지원할 수 있습니다. 자세한 내용은 SQL Server 7.0 온라인 설명서에 있는 "Application Design" 및 "Understanding and Avoiding Blocking" 항목을 참조하십시오. 수많은 사용자들이 접속하는 성공적인 사이트는 일반적으로 이 항목에서 설명한 기술을 사용합니다.

'MSSQL' 카테고리의 다른 글

(펌) MSSQL Server 백업 / 복구 시나리오  (0) 2011.10.20
(펌) MSSQL 2005 백업계획작성  (0) 2011.10.20
(펌) MSSQL Lock  (0) 2010.12.07
(펌) MSSQL2005 - New isolation level  (0) 2010.12.07
(펌) MSSQL SNAPSHOT 격리 수준  (0) 2010.12.07

출처 : http://blog.daum.net/lords001/582427

** LOCK **

트랜잭션은 LOCK 과 떨어져서 설명될 수 없는 관계이고, LOCK 도 트랜잭션 없이 설명될 수 없다.

LOCK 즉 잠금의 문제는 동시성 문제이다. 어떤 자원을 누군가 사용하고 있다면 어떤 형태로든 잠금이 설정된다. 그리고 그 잠금의 종류에 따라서, 해당 자원은 다른 사람이 어느 한계까지만을 사용할 수도 있고 아예 엑세스 자체도 불가능할 수도 있다.
그래서 여러 사용자가 함께 사용하는 응용프로그램이거나 특히 웹사이트 같은 경우엔 트랜잭션 사용 시 잠금에 관한 설정을 꼭 고려해야 한다.


1. LOCK 개념

LOCK 은 잠금이다. 잠금이란 어떤 자원(개체 등)에 대해 자신이 사용하고 있다는 것을 알리는 것이다.
그렇게 되면, 다른 연결이 해당 리소스를 엑세스할 때 제한을 받게 된다. 이렇게 하는 이유는 한 리소스를
한 사람만 변경하도록 함으로써, 데이터에 대한 일관성을 보장받기 위해서이다.
SQL Server 는 잠금기법을 사용하여 트랜잭션 무결성 및 데이터베이스 일관성을 유지한다.
잠금은, 다른 사용자가 변경 중인 데이터를 읽을 수 없게 하며 여러 사용자가 동시에 같은 데이터를 변경할 수 없게 한다. 잠금을 사용하지 않으면 데이터베이스 내의 데이터가 논리적으로 잘못될 가능성이 매우 크고, 이것은 데이터베이스 자체에 대한 심각한 오류가 된다.
우리는 이러한 잠금 매커니즘을 이해해서, 트랜잭션 제어 시에 참고하여 성능을 높이면서 동시성도 높일 수 있도록 해야 한다.

잠금의 종류는 여러 가지가 있지만 크게 공유 잠금(SHARED LOCK)과 배타적 잠금(EXCLUSIVE LOCK)으로 나눈다.

- 공유 잠금 : SELECT
- 배타적 잠금 : INSERT, UPDATE, DELETE

공유 잠금은 SELECT 문에 의해 설정되고, 배타적 잠금은 DML(INSERT,UPDATE,DELETE) 에 의해서 설정된다.

잠금은 서로 다른 사용자간의 자원쟁탈을 막기 위한 설정이므로, 이제부터는 두 CONNECTION을 예로 들면서 설명할 것이다. SESSION 1을 첫 번째 연결이라 하고, SESSION 2 를 두 번째 커넥션이라고 하자.

SESSION 1에서 실행하는 작업이 아래와 같다.

< SESSION1>

CREATE TABLE ISOL_1
(IID INT IDENTITY(1,1), INAME VARCHAR(10))
GO

BEGIN TRAN
INSERT ISOL_1 VALUES('AAA')
-- ISOL_1 테이블에 대해 잠금이 걸려 있다.
DBCC OPENTRAN
EXEC SP_LOCK 54


ROLLBACK TRAN

현재 하나의 트랜잭션이 진행중이고, 이 트랜잭션은 ISOL_1 테이블에 데이터를 입력하려는 작업이다.
이 때 INSERT 작업을 실행하는 ISOL_1 테이블에는 잠금이 설정되게 되는데, 여기 설정되는 잠금은 기본적으로 X 잠금이다.
X 는 EXCLUSIVE를 의미하여 잠금의 종류 중 하나로서, 배타적 잠금을 의미한다. 배타적 잠금은 어떤 연결도 해당 리소스(테이블 혹은 행 등)를 엑세스하지 못하도록 하는 잠금이다.

<SESSION2>

SELECT * FROM ISOL_1
-- ISOL_1 테이블을 보고자 하나, 현재 배타적 잠금이 SESSION1
-- 에 의해서 배타적 잠금이 걸려 있어서 볼 수 없다.

위의 SELECT 문은 현재 실행되지 못하고, 그렇다고 끝나지도 않고 있는 상태이다. 그러므로 이 SELECT 문을 실행한 사용자는 어떤 다른 명령도 실행할 수 없다. 그리고 이 상태는 영원히 계속된다.
이것이 배타적 잠금이다.
현재 다른 연결인 SESSION1에서 ISOL_1 테이블을 변경하는 작업을 하고 있으므로, ISOL_1 테이블에
배타적 잠금을 설정하고 있다. 그래서 다른 연결이 엑세스하는 시도는 BLOCKING 당하게 된다.
이 상태를 해제하려면 다음과 같은 두 가지 방법을 써야 한다.

- SESSION 1에서 해당 트랜잭션을 COMMIT /ROLLBACK 문으로 완료한다.
- 아니면, SESSION 1이나, SESSION 2 프로세스를 강제로 종료(KILL)한다.

이 중 우리는 트랜잭션을 완료하는 쪽으로 실행해 보겠다. 이제 SESSION 1에서 COMMIT 문을 실행하자.
그러면, ISOL_1 테이블에 대해서 잠금을 풀기를 기다리던 SESSION 2 연결의 SELECT 문은 즉시 실행된다.

< SESSION1>

CREATE TABLE ISOL_1
(IID INT IDENTITY(1,1), INAME VARCHAR(10))
GO

BEGIN TRAN
INSERT ISOL_1 VALUES('AAA')
-- ISOL_1 테이블에 대해 잠금이 걸려 있다.
SELECT @@TRANCOUNT


DBCC OPENTRAN
EXEC SP_LOCK 54

COMMIT TRAN


<SESSION2>

SELECT * FROM ISOL_1
-- ISOL_1 테이블을 보고자 하나, 현재 배타적 잠금이 SESSION1
-- 에 의해서 배타적 잠금이 걸려 있어서 볼 수 없다.

위에서(두번째 세션1 처리) COMMIT TRAN 문을 실행하자마자, 기다렸다는 듯이 SESSION 2의 명령은 실행된다. 실제로도 기다리고(BLOCKED) 있었던 것이 맞다.
이렇게 배타적 잠금은 다른 잠금을 허용하지 않는다. 즉 다른 배타적 잠금이나 공유 잠금을 시도하려는 모든 연결을 허용하지 않는다는 것이다. 이것을 호환이 되지 않는다고 말한다.
배타적 잠금은 다른 잠금과 호환되지 않는다.

이번엔 좀 다른 경우를 보자. 다시 그림1-1과 같은 트랜잭션을 실행한다.
그리고 이제 SESSION 2에서 다르게 명령을 내려본다. 먼저 그림1-5를 보자.
SESSION 2에서 실행하는 명령이다.

INSERT ISOL_1(INAME) VALUES('BBB')
INSERT ISOL_1(INAME) VALUES('CCC')

BEGIN TRAN
UPDATE ISOL_1
SET INAME = 'ZZZZ'
WHERE IID = 1
-- 1) ISOL_1 테이블에 대해 잠금이 걸려 있다.

-- 2) 현재 활성화된(커밋되지않은) 트랜잭션의 프로세스ID를 확인한다.
DBCC OPENTRAN

-- 3) 프로세스ID의 잠금 정보를 확인한다.
EXEC SP_LOCK 54

ISOL_1 테이블에 데이터 두 개를 더 입력하고 다시 트랜잭션을 시작해서 UPDATE 문을 IID 1번에 대해 실행하고 있다. 그리고 현재 설정된 잠금의 상태를 보기 위해서 EXEC SP_LOCK 명령을 사용하고 있다. 그런데, 그냥 SP_LOCK을 실행하면 모든 연결에 대한 잠금 정보가 모두 출력되어 보기가 불편하므로, DBCC OPENTRAN 명령을 사용해서 현재의 프로세스ID만을 구해서, 해당 프로세스 ID를 매개변수로 해서 SP_LOCK 명령을 실행하고 있는 것이다.

그 중 잠금에 대한 구체적인 정보가 TYPE 컬럼과 MODE 컬럼의 값이다. TYPE 은 잠금의 형태를 말하는 것이고, MODE 는 잠금의 종류를 말하는 것이다. 기본적으로 SQL Server 가 사용하는 잠금의 형태는 KEY 레벨 잠금 혹은 ROW 레벨 잠금이다. 사실 둘은 같은 뜻이며 둘 다 행 단위 잠금을 말한다. 다만 잠금이 걸리는 테이블에 클러스터드 인덱스가 있으면 KEY 레벨 LOCK 이라고 부르고, 클러스터드 인덱스가 없으면 ROW 레벨 LOCK 이라고 부를 뿐이다.
이것은 용어상의 문제일 뿐 결국 같은 뜻이다.
그래서, 만약 현재 걸린 잠금이 기본 잠금인 행 단위 잠금이라고 한다면, 다음 아래쿼리의 명령은 실행되어야 할 것이다. 그러나 실행은 실패하고 역시 BLOCKED 되게 된다.
아래 쿼리의 명령은 SESSION 1에서 실행하는 작업이다.

SELECT * FROM ISOL_1 WHERE IID = 4
-- 없는 데이터인 4번을 보려고 하고 있지만, ISOL_1 테이블자체에
-- 잠금이 걸려있으므로 여전히 BLOCKED 되고 있다.

SESSION 2에서는 IID 4번을 SELECT 하려고 시도하고 있다.
그리고 SQL Server 의 기본 잠금단위는 행 단위 잠금이다. 그렇다면, 현재 ISOL_1 테이블의 IID 1번에 대해서만 LOCK 이 설정되어 있다는 뜻이다. 우리가 보려고 하는 것은 없는 데이터 4번이다.
그러나, 보지 못하고 대기 상태에 있다.
잠금의 종류 즉 MODE 컬럼을 보자. 현재 개체ID 1220199397 에 대해서 어떤 잠금이 걸려져 있는가?

X 가 포함된 것은 모두 배타적 잠금이다. 개체ID 1220199397 에 대해서 현재,

- “TAB" 즉 테이블에 대해 배타적 잠금이,
- “PAG" 즉 데이터가 있는 페이지에 대해서 배타적 잠금이,
- “RID" = ROW ID 즉, 행에 대해서 배타적 잠금이

설정되어 있는 것을 확인할 수 있다.

만약 RID 에 대해서만 LOCK 이 걸려 있다면, 위의 SELECT 문은 실행되었을 것이다.
그러나 현재 TABLE 에 대해 배타적 잠금이 걸려져 있는 상태이기 때문에 위의 SELECT 문은 실행되지 않는 것이다. 일반적으로 어떤 개체에 변경작업을 하면 개체자체에 배타적 잠금을 건다는 것을 여기서 알 수 있겠다. 다시 SESSION 1을 COMMIT 해서 트랜잭션을 완료하자.

INSERT ISOL_1(INAME) VALUES('BBB')
INSERT ISOL_1(INAME) VALUES('CCC')

BEGIN TRAN
UPDATE ISOL_1
SET INAME = 'ZZZZ'
WHERE IID = 1
-- 1) ISOL_1 테이블에 대해 잠금이 걸려 있다.

-- 2) 현재 활성화된(커밋되지않은) 트랜잭션의 프로세스ID를 확인한다.
DBCC OPENTRAN

-- 3) 프로세스ID의 잠금 정보를 확인한다.
EXEC SP_LOCK 54

-- 4) 트랜잭션 완료
COMMIT TRAN
-- 5) 현재 연결에 대해 잠금정보 표시
EXEC SP_LOCK 54

SESSION 1의 트랜잭션을 COMMIT 하고 다시 위의 명령을 실행해서 잠금 정보를 확인하면, 현재 ISOL_1 테이블에 걸려있는 잠금은 하나도 없음이 확인된다.



1) 잠금의 단위

SQL Server 가 잠금을 설정할 수 있는 개체의 단위에는 다음과 같다.

1] RID : 행 식별자이다. 테이블 내에서 행 하나를 잠글 때 사용된다.
2] 키(key) : 인덱스가 있을 때의 행 잠금단위이다. 키 범위를 보호하기 위해 사용한다.
3] 페이지(page) : 8KB 데이터 페이지 또는 인덱스 페이지이다.
4] 익스텐트(extent) : 인접한 8개의 데이터 페이지 또는 인덱스 페이지 이다.
5] 테이블 : 데이터와 인덱스가 포함된 전체 테이블을 뜻한다.
6] DB : 데이터베이스
위와 같은 단위에 대해서 SQL Server 는 상황에 따라 공유 잠금, 배타적 잠금등을 설정할 수 있다.

2) 잠금의 종류

SQL Server 의 잠금의 종류는 다음과 같다.

1] SHARED(S) - 읽기 동안에만 일어나며, 다른 S 락에 대해서는 공유하나 X 에 대해서는 배타적이다.
데이터를 변경하거나 업데이트하지 않는 작업(읽기 전용 작업)에 사용한다.
공유(S) 잠금을 사용하면, 여러 트랜잭션이 동시에 하나의 개체를 읽을(SELECT) 수 있다.
즉 공유 잠금은 다른 공유 잠금과 호환된다. 공유 잠금끼리는 서로 충돌되지 않는 것이다.
그러나, 특정 리소스에 공유 잠금이 설정되어 있는 동안에는, 다른 트랜잭션이 데이터를 변경할 수는 없다. 즉 공유 잠금은 다른 배타적 잠금을 허용하지 않으며 호환되지 않는다고 말한다.
한편, 리소스에 대한 공유 잠금은 다음 경우를 제외하고는, 데이터를 읽자마자 바로 해제된다.
- 트랜잭션 고립 수준을 REPEATABLE 레벨 이상으로 설정
- LOCK HINT를 써서, 해당 공유 잠금을 트랜잭션 끝까지 유지한다.
LOCK HINT를 이용하면 잠금은 계속 유지된다.


BEGIN TRAN
-- 1) 1번부터 10번까지 SELECT..
SELECT * FROM EMP
WHERE EID BETWEEN 1 AND 10
-- 그러나, 1) 의 SELECT 문은 공유잠금이 풀린상태이다.

위의 쿼리는 트랜잭션을 시작하고, 직원테이블의 직원ID(EID)가 1번~10인 데이터를 조회하는 명령을
실행한 것이다. 그러나 1)명령으로는 이 예제를 성공적으로 확인할 수 가 없다. 그것은 이미 잠금이 풀렸기 때문이다. 1)의 SELECT 문을 실행하면 바로 결과를 볼 수 있다. 그 결과가 출력되는 동안만 잠금이 걸리는 것이다. 아주 짧은 시간이다. 이것이 SELECT 문에 걸리는 잠금이다. 그러므로 위의 쿼리의 SP_LOCK 을 실행하면, EMP 테이블에 대해 아무런 잠금도 걸려있지 않은 것을 확인할 수 있을 것이다.

그러나, 잠금을 유지하도록 하는 잠금 힌트가 있다. 바로 HOLD LOCK 옵션이다. 이 옵션을 실행하면 SELECT 문을 실행하는 동안에만 잠금이 설정되었다고 결과가 출력되면 바로 끝나는 것이 아니라, 배타적 잠금처럼 트랜잭션이 끝날 때까지 공유 잠금이 유지된다. 아래의 쿼리가 바로 그 예이다.

<SESSION1>
BEGIN TRAN
-- 1) 1번부터 10번까지 SELECT..
SELECT * FROM EMP ( HOLDLOCK)
WHERE EID BETWEEN 1 AND 10
-- 공유 잠금은 계속 유지되고 있다.

SP_LOCK

잠금이 계속 유지되고 있으므로 이번엔 SP_LOCK 명령에서 EMP 테이블에 걸린 잠금 정보에 대한 결과물이 출력된다. RangeS-S 잠금은 BETWEEN 명령 때문에 생긴 것이다. EID 1번~10번까지의 범위가 잠금이 설정되어 있기 때문에, 해당 데이터 10개에 대해서 각각 KEY 레벨 잠금이 RangeS-S 로 설정되어 있는 것이다. 모두 S LOCK 이다. 그리고 TABLE 레벨과 PAGE 레벨에도 전부 공유 잠금이 유지되고 있는 것을 볼 수 있다. 그러면, 공유 잠금에 대해 다른 연결이 실행하는 것을 확인할 수가 있겠다.

<SESSION2>
SELECT * FROM EMP

이상 없이 EMP 테이블전체에 대한 SELECT 문이 실행되는 것을 확인할 수 있다. 테이블 단위, 페이지 단위, 행 단위 모두 공유 잠금이기 때문에 다른 공유 잠금과 호환이 되어서 실행이 제대로 된다.
그러면, SESSION 2에서 다음과 같이 실행해 보자.

<SESSION2>
-- 1) EMP 테이블 전체를 조회
SELECT * FROM EMP

-- 2) EMP 테이블의 5번 데이터를 수정
UPDATE EMP
SET SAL = 900000
WHERE EID = 5

EID 5번에 대해서 데이터를 수정하고 있다. EID 5 번은 그러나 SESSION 1에 의해 현재 공유 잠금이 걸려있는 상태다. 그래서 해당 데이터는 KEY 레벨 공유 잠금이 설정되어 있기 때문에 수정할 수 없는 것이다. 이것을 공유 잠금은 배타적 잠금과 호환되지 않는다고 말한다. 역시 EID 1번~10번외의 데이터를 수정하려 해도, 테이블전체에 공유 잠금이 걸려있기 때문에 데이터 수정은 실패하게 된다.

이제 LOCKING 상태를 해결해야 하는데, 먼젓번엔 트랜잭션을 COMMIT 하는 방법으로 해결했지만, 이번에는 다른 방법으로 해결해보도록 하겠다.


프로세스 관리
EM에서 관리-현재동작-잠금/프로세스 ID를 보면 잠금 정보를 그래피컬하게 확인할 수 있다.

잠금 정보를 보니 현재 51번,52 번 프로세스가 빨간 색으로 표시가 되어 있는데, 그중 51번 프로세스는
차단주체(BLOCKED) 라고 되어 있고, 52번 프로세스는 차단하는 중(BLOCKING)이라고 되어있다.
어떤 것이 잠금을 설정한 프로세스이고 어떤 것이 해당 잠금 때문이 실행이 거부되고 있는 프로세스일까?
물론 해당 프로세스를 더블클릭하면 정보를 볼 수 있다. 그러나, 여기서 충분히 예상가능하다.

느낌표(!)가 표시되어 있는 것이 현재 잠금을 설정한 프로세스이다. 즉 “SELECT ...(HOLDLOCK)...“을
실행한 명령임을 알 수 있다. 그리고 목에서 출혈(?)이 나고 있는 듯한 그림을 가진 프로세스가 바로 엑세스를 거부당한 프로세스이다. 그럼 BLOCKED 된 프로세스인 51번 프로세스를 더블클릭해서 확인해 보자.

--EM 에서 직접 정보들을 보면서 비교

프로세스 중지” 단추를 누르면, 해당 프로세스 51번이 KILL 되면서 UPDATE 문은 ROLLBACK 된다.
프로세스를 KILL 할 수 있는 권한은 기본적으로 SYSADMIN 서버역할과 DB_OWNER 서버역할의 멤버들, 그리고 PROCESSADMIN 서버역할의 멤버들에게만 부여된다.
또 프로세스를 KILL 하는 명령은 “KILL" 명령으로 실행할 수 도 있다. 단 해당 트랜잭션 내에서는 KILL 명령을 사용할 수 없다.

KILL 52


* LOCK_TIMEOUT 옵션

잠금이 걸린 상태를 생각해 보자. HOLDLOCK 옵션에 의한 SELECT 문의 공유 잠금은 트랜잭션이 끝날 때까지 계속 유지될 것이고, 그러므로 SESSION2의 UPDATE 문은 SESSION1의 트랜잭션이 끝날 때까지 계속 거부되어 사용할 수 없을 것이다. 그래서 이런 상황에서 SESSION1의 트랜잭션이 끝나거나 KILL 되길 무조건 기다리는 것이 아니라, 얼마동안만 기다렸다가 그 기간이 끝나면 SESSION1의 트랜잭션이 끝나지 않아도 자동으로 기다리기를 멈추고 작업을 종료하게끔 할 수 있다는 것이다.
그것을 가능하게 하는 명령이 LOCK_TIMEOUT 명령이다.

<SESSION1>
BEGIN TRAN
-- 1) 1번부터 10번까지 SELECT..
SELECT * FROM EMP ( HOLDLOCK)
WHERE EID BETWEEN 1 AND 10
-- 공유 잠금은 계속 유지되고 있다.

SP_LOCK


<SESSION2>
-- 1) EMP 테이블 전체를 조회
SELECT * FROM EMP

-- 2) EMP 테이블의 5번 데이터를 수정
UPDATE EMP
SET SAL = 900000
WHERE EID = 5

<LOCK_TIMEOUT>
-- 5초만 기다렸다가, 자동으로 프로세스를 중자하게 한다.
-- 1000 은 1초를 뜻한다.
SET LOCK_TIMEOUT 5000

UPDATE EMP
SET SAL = 900000
WHERE EID = 5

SESSION2 대신 위와 같이, 먼저 해당 연결에 대해서 LOCK_TIMEOUT 설정을 하고, 트랜잭션을 실행하게 되면, BLOCKED 되었다고 하더라도 마냥 기다리지 않고 설정한 시간 후에 바로 실행을 중지하게 된다.
이것은 잠금에 대한 대기시간을 짧게 함으로써, 다른 명령을 실해할 수 있도록 SESSION을 OPEN 하는 결과를 가져온다. 대기시간이 길어질수록 사용자는 시스템의 성능자체를 의심하게 될 것이다.
시간의 설정은 밀리세컨드 단위로 설정된다.
-- 쿼리분석기 하단에서 확인
기본적으로 모든 사용자에게 SET LOCK_TIMEOUT 사용 권한이 부여된다.

또한, 위와 같이 설정해서 LOCK_TIMEOUT 설정보다 오래 기다린 경우, 차단된 명령문은 자동으로 취소되고 오류 메시지 1222 "잠금 요청 제한 시간이 초과되었습니다"가 응용 프로그램으로 반환된다.
그러나 명령문을 포함하는 모든 트랜잭션이 SQL Server에 의해 ROLLBACK 되거나 취소되는 것은 아니다. 따라서 응용 프로그램에는 오류 메시지 1222를 잡을 수 있는 오류 처리 루틴이 있어야 한다.
응용 프로그램이 오류를 잡지 않으면 트랜잭션 내의 각 명령문이 취소된 것을 모른 채 계속 진행하여 트랜잭션의 명령문이 나중에 실행되지 않은 명령문을 참조할 경우 오류가 발생할 수 있기 때문이다.

-- 5초만 기다렸다가, 자동으로 프로세스를 중자하게 한다.
-- 1000 은 1초를 뜻한다.
SET LOCK_TIMEOUT 5000

UPDATE EMP
SET SAL = 900000
WHERE EID = 5

IF @@ERROR = 1222
BEGIN
EXEC MASTER..XP_CMDSHELL 'NET SEND HAN 수정실패'
-- WINDOWS 2000의 EVENT LOG 에 해당 에러를 기록한다.
RAISERROR ( 'EMP 에 대한 SAL 컬럼수정이 실패함',
10,1) WITH LOG
END
GO


2] EXCLUSIVE(X)

배타적(X) 잠금이 설정되면 동시에 여러 연결이 한 리소스에 액세스할 수 없게 된다. 오직 하나의 연결만이 해당 리소스를 점유한다. 이 잠금은 수정 시에, 행단위로 설정되게 된다.
예를들어 누군가 특정 직원테이블의 데이터 중 “한창현”이라는 데이터를 변경 (insert, update, delete)하고 있다고 하자. 그 상황에서 배타적 잠금이 해당 테이블(직원)의 해당 행(한창현인 데이터)에 걸리게 되는 것이다. 그러면 해당 행을 엑세스하려는 모든 다른 연결은, 그것이 읽기 위한 것이든 수정을 위한 것이든 모두 거부되고 Blocking 당하게 된다.

배타적 잠금으로 잠근 데이터는 다른 트랜잭션이 읽거나 수정할 수 없다. 이것은 공유 잠금에도 적용된다.
배타적 잠금은 그래서 공유 잠금을 포함한 대부분의 락에 대해서 배타적이고 호환되지 않는다.

<SESSION1>
BEGIN TRAN
UPDATE EMP
SET SAL = 400000
WHERE EID = 1


<SESSION2>
-- 1) 해당 데이터를 엑세스하려 하므로, BLOCKED 됨
SELECT * FROM EMP WHERE EID = 1

-- 2) EID 1번을 엑세스하려는 것이 아니므로, 실행됨
SELECT * FROM EMP WHERE EID = 8

SESSION1에서 EID 1번에 대해서는 엑세스가 안되지만, 8번에 대해서는 무리없이 실행된다.
이것은 SQL Server 가 기본적으로 행하는 잠금이, 행 단위 잠금이라는 것을 확실히 보여준다.


3] UPDATE(U)

수정 시에 X 락을 걸기 전 데이터영역을 읽기 위해 거는 락이다. 해당 리소스에 대한 잠금을 미리 체크하여 데드락(교착상태)을 방지하기 위해서 사용된다. 공유 잠금과 호환된다.

업데이트(U) 잠금을 사용하면 일반적인 형태의 교착 상태가 방지된다.
일반적인 업데이트 패턴은 레코드를 읽고, 리소스(페이지 또는 행)에 대한 공유(S) 잠금을 얻은 다음 행을 수정하는 트랜잭션으로 구성되는데, 행을 수정할 때는 먼저 배타적 잠금으로 잠금을 변환해야 한다.
두 트랜잭션에서 리소스에 대해 공유 모드 잠금을 얻은 다음 데이터를 동시에 업데이트하려고 하면 한 트랜잭션이 배타적 잠금으로 잠금을 변환하려 할 것이다. 이 때, 한 트랜잭션의 배타적 잠금은 다른 트랜잭션의 공유 모드 잠금과 호환되지 않으므로, 공유 모드를 단독 모드로 변환할 때는 잠금 대기가 발생하게 된다. 그리고 두 번째 트랜잭션도 해당 업데이트에 대해 배타적 잠금을 얻으려고 할 것이다. 이 경우 두 트랜잭션 모두 배타적 잠금으로 변환 중이고 각각 상대 트랜잭션이 공유 모드 잠금을 해제하기를 기다리므로 교착 상태가 발생하는 것이다.

이러한 교착 상태를 방지하기 위해서 SQL Server는 업데이트 잠금을 사용한다. 이 업데이트 잠금은 한 번에 한 트랜잭션만 리소스에 대한 업데이트 잠금을 얻을 수 있게 하기 때문에 교착상태가 방지될 수 있다. 트랜잭션이 리소스를 수정하면 업데이트 잠금이 배타적 잠금으로 변환되고 그렇지 않으면 잠금이 공유 모드 잠금으로 변환된다. 그러나 업데이트 잠금을 사용하더라도 교착상태를 완전히 방지할 수는 없다. 그것은 모든 RDBMS 에서도 마찬가지이다.

4] INTENT(I)

공유 잠금 OR 배타적 잠금이 걸린 특정 데이터영역에 표시하는 알림간판이라고 생각하면 된다.
의도적 잠금이라고 하며 다른 INTENT 잠금 들과 호환된다.

의도적 잠금은 SQL Server가 리소스에 대해 공유 잠금 또는 배타적 잠금을 얻으려 할 때 같이 발생한다.
예를 들어, 의도적 공유 잠금(IS)을 테이블 수준에서 설정하려고 한다면, 이것은 해당 트랜잭션이 해당 테이블의 페이지 또는 행에 대해 공유 잠금을 설정하려고 한다는 것을 의미한다.
이렇게 테이블 수준에서 의도적 잠금을 걸면, 이후에 다른 트랜잭션이 해당 페이지를 포함하는 테이블에
대해 배타적 잠금을 얻을 수 없게 된다.

SQL Server는 테이블 수준에서만 의도적 잠금을 확인하여, 트랜잭션이 해당 테이블에 대해 잠금을 얻을 수 있는지 확인하므로 의도적 잠금을 사용하면 성능이 향상된다. 왜냐하면, 직접 테이블의 모든 행 또는 페이지 잠금을 확인할 필요가 없기 때문이다.
생각을 해보자. 우리가 살고 있는 집엔 방(TABLE)이 있다. 그 방 중 어떤 방에 들어가서 방을 사용하려고
하는 데, 벌써 누군가 그 방을 쓰고 있다. 이때 그 방을 쓰는 사람이 방문에다 “방청소 중“ 이라고 방문에다 알림판을 하나 걸어 놓는 다면, 우리는 굳이 그 방을 두드릴 필요도 없고 방문을 열어볼 필요도 없다.
다만 방청소가 끝날때까지(트랜잭션 완료) 방바깥에서 기다리면 되는 것이다. 그만큼 우리가 덜 움직여도 된다는 것이다. 그럼으로 해서 성능이 향상되는 효과를 볼 수 있다.

BEGIN TRAN
UPDATE EMP
SET SAL = 1
WHERE EID = 1
DBCC OPENTRAN
EXEC SP_LOCK 51 -- 현재 EMP 테이블의 개체ID는 695673526 이다.
ROLLBACK

의도적 잠금은 세 가지로 나누어지는 데, 그 종류는 다음과 같은 것들이 있다.

- 의도적 배타적 잠금(IX)
잠금을 걸려는 트랜잭션이 각 리소스계층(테이블,페이지,행등...)에 대해 X 잠금을 설정하여 계층의 아래쪽에 있는 일부 리소스를 수정하려 하는 것을 말한다. IX는 IS의 상위 집합이 된다.

- 공유 및 의도적 배타적 잠금(SIX)
잠금을 걸려는 트랜잭션이 각 리소스계층에 대해 IX 잠금을 설정하여 계층의 아래쪽에 있는 모든 리소스에 대해서는 읽기 작업을 하고, 일부 리소스에 대해서는 수정작업을 하려고 하는 것을 말한다.
최상위 수준 리소스에서는 동시 IS 잠금이 허용된다.
예를 들어, 테이블에 대한 SIX 잠금은 테이블에 대해 SIX 잠금을 설정하여 동시 IS 잠금을 허용하고, 수정 중인 페이지에 IX 잠금을 설정하고 수정된 행에 대해서는 X 잠금을 설정한다. 각 리소스 당 한 번에 하나의 SIX 잠금을 설정할 수 있으므로 다른 트랜잭션이 테이블 수준에서 IS 잠금을 얻어 계층 아래쪽에 있는 리소스를 읽을 수 있게 된다. 그라나, 그 상황에서 다른 트랜잭션이 리소스를 수정할 수는 없게 된다


5] SCHEMA(Sch)

스키마 잠금은 다음 두 가지로 나누어서 설명된다.

- Sch-M (Schema Manipulation)
스키마를 변경하는 작업 즉, DDL 문 실행 시에 SQL Server 가 SCHEMA 자체에 대해서 건다.
이 잠금은 모든 잠금에 대해서 배타적이며, 어떤 작업도 허락하지 않는다.
그도 그럴것이 데이터베이스의 구조를 변경하는 누군가 그 데이터베이스 내의 데이터를 엑세스해서는 안될 것이기 때문이다. 이것을 스키마 변경 잠금이라고 한다.

- Sch-S (Schema Stability)
쿼리문 컴파일 시에만 발생한다. S or X 와 호환된다. 스키마 안정성(Sch-S) 잠금은 의도적 잠금 등 다른 트랜잭션 잠금을 차단하지 않는다. 따라서 쿼리가 컴파일되는 동안에도, 테이블에 대한 의도적 잠금을 포함하여 다른 트랜잭션을 계속 실행할 수 있다. 그러나 해당 테이블에서의 DDL 작업은 수행할 수는 없다.

참조) 그림.. 호환성.bmp

SQL Server 2000은 한 트랜잭션으로 여러 유형의 리소스를 잠글 수 있는 세분화된 잠금의 단계를 제공한다. 잠금에 대한 비용을 최소화하기 위해 SQL Server는 자동으로 해당 작업에 맞는 수준에서 리소스를 잠근다. 행과 같이 작은 수준에서 잠그면 동시성이 많이 향상된다. 그러나, 많은 행을 잠글 경우엔 더 많은 잠금을 보유해야 하므로 오버헤드가 늘어날 수 밖에 없다.
이때 테이블과 같이 큰 수준에서 잠그면 테이블자체를 잠궈서 다른 트랜잭션이 테이블자체를 액세스하지 못하게 제한하므로, 동시성은 떨어지지만 처리할 잠금 수가 적으므로 오버헤드는 줄어들게 된다. 이것은 상황에 따라 SQL Server 의 선택은 자동으로 처리된다. SQL Server 의 기본 잠금 단위는 행 단위 잠금이다.

잠금의 단계에 따른 동시성과 비용을 그래프로 확인.
참조) 그림.. 잠금대비동시성.bmp


2. 잠금의 수준

잠금은 그 레벨에 대해서 SQL-92 표준을 가지고 있다. 이것은 SQL Server 에만 적용되는 것이 아니고, 모든 RDBMS에서 자신들의 제품에 맞도록 적용하는 기준이다. 잠금수준의 표준은 네 가지가 있으며 SQL Server 2000 은 네 가지 모두를 지원한다. 오라클은 그 중 두 수준만을 지원한다.
이러한 잠금 수준은 트랜잭션 고립수준, 혹은 트랜잭션 격리수준 혹은 TRANSACTION ISOLATION LEVEL 이라고 부른다.

다음은 잠금 수준에 대한 내용을 설명한 것인데, 그 수준은 다음과 같이 레벨을 가진다.
오른쪽으로 갈수록 잠금의 정도가 심함을 나타낸다.

READ UNCOMMITTED < READ COMMITTED < REPEATABLE READ < SERIALIZABLE

1) READ COMMITTED

가장 낮은 단계인 READ UNCOMMITTED부터 설명해야 함이 옳으나, READ COMMITTED 가 SQL Server 의 기본 잠금 레벨이므로, READ COMMITTED부터 설명하는 것이 이해가 더 빠르리라 생각된다.
READ COMMITTED 는 말그대로, COMMIT 된 트랜잭션만을 읽자는 것이다.
이것은 잠금을 건 트랜잭션에서의 입장이 아니다. 잠금을 건 트랜잭션에 대해서 SELECT 하려는 또 다른 트랜잭션에서의 입장이다. 현재는 해당 트랜잭션에 대해 잠금이 걸려 있으면 해당 잠금의 종류에 따라 볼 수도 있고 보지 못할 수도 있다. 공유 잠금에 대해서는 볼 수 있으며, 배타적 잠금이 걸려 있으면 볼 수 없는것이 현재 상태이다. SELECT 한다는 것은 공유 잠금이고, 공유 잠금은 공유 잠금과는 호환되지만 배타적 잠금에 대해서는 호환되지 않기 때문이다.

이것이 READ COMMITTED 의 뜻이다. 배타적 잠금에 대해서 공유 잠금을 허용하지 않겠다는 뜻이다.
SQL Server 의 기본 잠금 수준이기 때문에 현재, SESSION1과 SESSION2의 1)번을 실행했을 때의 상황이 발생하게 되는 것이다.
----------------------------------
<SESSION1>
BEGIN TRAN
UPDATE EMP
SET SAL = 400000
WHERE EID = 1


<SESSION2>
-- 1) 해당 데이터를 엑세스하려 하므로, BLOCKED 됨
SELECT * FROM EMP WHERE EID = 1

-- 2) EID 1번을 엑세스하려는 것이 아니므로, 실행됨
SELECT * FROM EMP WHERE EID = 8
---------------------------------------

<SESSION1>
BEGIN TRAN
UPDATE EMP
SET SAL = 80000
WHERE EID = 1
SP_LOCK -- 배타적 잠금이 걸려 있음이 확인된다


<SESSION2>

SELECT * FROM EMP
WHERE EID = 1
-- SESSION1에서 EMP 테이블에 배타적 잠금을 설정했고,
-- 트랜잭션이 끝나지 않았으므로, 볼수 없다.
-- 잠금 설정이 되지 않은 나머지 행들은 볼 수 있다.

SESSION1에서 현재 개체ID 695673526 에 대해 KEY 단위로 X 잠금이 설정되어 있는 것을 볼 수 있다.
물론 이 개체에는 PAGE 와 TABLE 단위에 IX 잠금이 설정되어 있으나, 이 IX 잠금은 IS 잠금과 호환이
되기 때문에 다른 트랜잭션에서의 SELECT 작업을 허용한다. 해당 트랜잭션에서 IX 잠금이 설정되면, 업데이트 중인 행을 제외한 일부 행을 다른 트랜잭션이 읽거나 업데이트할 수 있다.
SESSION2의 예제에서는 해당 행을 보려고 했기 때문에 대기 상태가 된것이다.


2) READ UNCOMMITTED

READ UNCOMMITTED 수준은 잠금의 4가지 레벨 중 가장 낮은, 즉 가장 느슨한 단계의 잠금 수준이다.
이 수준은 특정 트랜잭션에서 배타적 잠금을 설정하고 있어도, 해당 데이터를 볼 수는 있게 한다.
물론 해당 리소스에 대한 수정작업은 모든 잠금 수준에서 불가능하다.
READ UNCOMMITTED는 배타적 잠금이 설정된 리소스(데이터)라고 하더라도, 볼 수(SELECT) 있게 하는, 잠금 중 가장 느슨한 단계이다. 배타적 잠금을 공유 잠금과 호환되게 하는 것이라고 보면 되겠다.
먼저 예제를 위한 테이블 만들기이다.

CREATE TABLE ISOL_2( IID INT PRIMARY KEY , INAME CHAR(5))
GO
INSERT ISOL_2(IID,INAME) VALUES(1, 'AAAAA')
INSERT ISOL_2(IID,INAME) VALUES(2, 'BBBBB')
INSERT ISOL_2(IID,INAME) VALUES(3, 'CCCCC')
INSERT ISOL_2(IID,INAME) VALUES(6, 'FFFFF')
INSERT ISOL_2(IID,INAME) VALUES(7, 'GGGGG')
INSERT ISOL_2(IID,INAME) VALUES(8, 'HHHHH')
INSERT ISOL_2(IID,INAME) VALUES(9, 'IIIII')
INSERT ISOL_2(IID,INAME) VALUES(10, 'JJJJJ')
GO
SELECT * FROM ISOL_2

8 개의 데이터가 ISOL_2 테이블에 입력되어 있으며, 중간에 IID 컬럼값 4번과 5번 데이터는 비어있다.
다음 예제는 트랜잭션 잠금 수준을 변경하고 트랜잭션을 시작하는 예제이다.

<SESSION1>

-- 1) IID 10번 행에 대해, 배타적 잠금을 발생하는 트랜잭션 실행
BEGIN TRAN
UPDATE ISOL_2
SET INAME = 'ZZZZZ'
WHERE IID = 10
EXEC SP_LOCK

SESSION1을 보면, 현재 1) 번의 명령을 실행했기 때문에, IID 10번에 대해 배타적 잠금이 설정되어 있다.
이번에도 다른 연결을 열어서 해당 트랜잭션에 대해 접근을 해보자.

<SESSION2>
-- 1) 트랜잭션 잠금 수준을 READ UNCOMMITTED 로 변경
SET TRANSACTION ISOLATION LEVEL
READ UNCOMMITTED
-- 2) 트랜잭션 고립수준 확인
DBCC USEROPTIONS
GO
-- 3) 배타적 잠금이 설정된 IID 10번 행을 SELECT 한다.
SELECT * FROM ISOL_2 WHERE IID = 10
-- 4) 테이블의 모든 행을 SELECT 한다.
SELECT * FROM ISOL_2

트랜잭션 고립 수준, 즉 잠금 수준을 변경하는 명령은,
"SET TRANSACTION ISOLATION LEVEL ..." 이다. 이 명령을 실행하고, 현재 연결에 대한 트랜잭션
고립 수준을 확인하려면, "DBCC USEROPTIONS"를 사용하면 된다.
"SET TRANSACTION ISOLATION..." 명령을 한 번이라도 실행하지 않으면, "DBCC USEROPTIONS"명령을 실행하더라도 잠금 수준 정보를 볼 수 없다.
그러나 그것은 기본값인 READ COMMITTED 로 설정되어 있다는 말이므로 혼동할 필요는 없다.

SESSION 2에서 3)번을 실행하건 4)번을 실행하건, 모두 잘 실행되고 있음을 볼 수 있다.
이것은 SESSION 2 트랜잭션이 SESSION 1 트랜잭션의 ISOL_2 개체의 각 행에 대해 공유 잠금을
잘 설정했다는 뜻이다.
READ COMMITTED 설정에서는 허락되지 않는 일이었지만, READ UNCOMMITTED 에서는 느슨하게
단계를 낮춤으로서 보는 것이 가능하도록 되어 있다


3) REPEATABLE READ

읽는 데이터의 일관성에 관한 얘기이다. 잠금이 설정되어 있을 때, 트랜잭션 시작 전과 시작 후의 데이터를 동일하게 읽게 함으로써, 읽는 데이터의 일관성을 보장하는 방법이 된다.
<SESSION1>
BEGIN TRAN
UPDATE ISOL_2
SET INAME = 'YYYYY'
WHERE IID = 8
DBCC OPENTRAN
EXEC SP_LOCK 51


commit tran

SESSION1에선 현재 SESSION 1 연결이 트랜잭션을 시작하고, IID 8번에 대해서 수정작업을 실행하고 있다. 그러므로 이 행엔 배타적 잠금이 설정되어 있다.

<SESSION2>
-- 1) 트랜잭션 잠금 수준을 READ UNCOMMITTED 로 변경
SET TRANSACTION ISOLATION LEVEL
READ COMMITTED
-- 2) 트랜잭션 고립수준 확인
DBCC USEROPTIONS
GO
-- 3) 배타적 잠금이 설정되지 않은 IID 7 번 행을 SELECT 한다. 잘 실행된다.
SELECT * FROM ISOL_2 WHERE IID = 7
-- 4) 잠금이 설정된 8번행을 포함한 범위검색을 한다. 대기상태가 된다...
SELECT * FROM ISOL_2
WHERE IID BETWEEN 7 AND 10

SESSION 1에서 실행한 수정 때문에 배타적 잠금이 설정되어 있으므로, SESSION2의 명령은 거부되고
대기중인 상태이다. 이것이 READ COMMITTED 잠금 수준이다.

그러나, 만약 SESSION 1이 시작되기 전에 SESSION 2의 4)번 명령을 실행했다고 생각해보자.
그때는 잠금이 없으므로 잘 보일 것이다. 그리고 SESSION 1의 명령을 실행하고 또 보자.
이 때는 보이지 않는다. 그렇다고 해서 READ UNCOMMITTED 로 설정하면 볼 수는 있지만, 해당 정보는 불확실한 정보가 된다는 것이다.
즉, READ UNCOMMITTED 로 설정했을 때 보는 것은 가능하지만, 그때 보는 데이터는 SESSION 1에서 COMMIT을 하느냐 ROLLBACK을 하느냐에 따라서 결정되지 않은 데이터를 보는 것이다.
SESSION 1에서 COMMIT을 했으면 다행이 제대로 된 데이터를 보는 것이지만, SESSION 1에서 ROLLBACK을 했다면 SESSION 2의 4)에서 본 데이터는 결국은 잘못된 정보인 것이다.

오라클의 잠금 수준은 기본적으로 READ UNCOMMITTED 로 되어 있기 때문에 이것을 가능하게 한다.
그러나, SQL Server 는 가능하지 못하도록 READ COMMITTED 가 기본설정값이다.
그래서 REPEATABLE READ 수준이 있다. REPEATABLE READ 로 잠금 수준을 설정하면, 트랜잭션이 아직 끝나지 않았을 때 변경전과 똑같은 데이터를 보도록 한다.
데이터가 변경된 것이 확실하게 완료되기 전까지는, 변경이 적용되기 전의 데이터를 일관성있게 보게 하겠다는 것이다. 그러므로 이것은 좀 더 데이터베이스 일관성을 보장해 준다.
그러므로 아래의 쿼리에서 SELECT 한 데이터는 변경되기 전의 데이터를 보여준다.

<SESSION2>
-- 1) 트랜잭션 잠금 수준을 REPEATABLE READ 로 변경
SET TRANSACTION ISOLATION LEVEL
REPEATABLE READ
-- 2) 트랜잭션 고립수준 확인
DBCC USEROPTIONS
GO

-- 3) 잠금이 설정된 9번행을 포함한 범위검색을 한다.
-- SESSION 1의 변경이 적용되기 전의 데이터를 보여준다...
SELECT * FROM ISOL_2
WHERE IID BETWEEN 7 AND 10

REPEATABLE READ 수준은 트랜잭션 진행 중인 개체에 엑세스해서 데이터를 읽으려고 할 때, 읽고자 하는 데이터에 일어나고 있는 변경이 성공될지 취소될지 불확실한 상태에서, 확실하게 트랜잭션이 완료되기 전까지는, 변경전의 데이터를 읽게 함으로써 데이터의 일관성을 보장해 주는 기법이다.


4) SERIALIZABLE

데이터의 범위에 대한 입력작업에 관한 이야기이다. 현재 ISOL_2 테이블엔 4번과 5번 데이터가
존재하지 않는다. 여기에 우리는 얼마든지 4번과 5번 데이터를 입력할 수 있다.
그러나 누군가 이 범위를 읽고 있다면, 이 입력은 위험하다.

<SESSION1>
-- 1~8번까지의 범위를 조회
BEGIN TRAN
SELECT * FROM ISOL_2
WHERE IID BETWEEN 1 AND 8
-- 4,5번은 현재 존재하지 않는다...
현재 보는 데이터는 4,5번을 제외한 6개의 데이터이다. 이 때 공유 잠금이 설정되어 있다.
트랜잭션 내에서의 작업이긴 하지만, 이미 SELECT 작업은 종료되었으므로 누군가 새롭게 4번데이터를 입력하려 한다면 이것은 성공한다.

<SESSION 2>
-- SESSION1에서 공유잠금을 건 범위내에 데이터를 입력한다.
INSERT ISOL_2(IID, INAME)
VALUES(4, 'DDDDD')
GO
SELECT * FROM ISOL_2 -- 입력되었다.

그런데, 문제가 있다. 현재 SESSION 1의 트랜잭션은 끝나지 않은 상태이다. 그렇다면, 현 상황이라면 아까 실행해서 본 결과는 의미없게 된다. 데이터가 부정확하게 된 것이다.
이러므로 데이터의 일관성을 보장받지 못하게 되는 결과를 낳게 된다.
그래서 이러한 데이터불일치를 막기 위해서, SERIALIZABLE 설정을 할 수 있다.

<SESSION1>
SET TRANSACTION ISOLATION LEVEL
SERIALIZABLE
-- 1~8번까지의 범위를 조회
BEGIN TRAN
SELECT * FROM ISOL_2
WHERE IID BETWEEN 1 AND 8
-- 5 번은 현재 존재하지 않는다...

--ROLLBACK

위의 상황은 잠금 수준 설정을 하고, 다시 SELECT를 1~8번에 대한 범위 내에서 실행하고 있는 상황이다. 이 때 잠금 수준 설정을 보는 쪽에서 한다는 것에 유의하자.
SESSION 2에서의 작업을 확인한다.

<SESSION2>
INSERT ISOL_2(IID, INAME)
VALUES(5, 'EEEEE')
-- 더 이상 해당 범위내에는 입력하지 못한다...

SESSION2에서 확인한 바로는, 이제는 범위 내에 데이터를 입력하지 못한다. 이것이 잠금 수준에서의 최고 설정이다. 가장 느슨한 레벨인 READ UMCOMMITTED 이고 제일 엄격한 것이 이 SERIALIZABLE 이다.
이렇게 네 가지의 잠금 수준 설정이 있지만, 사용자가 제어할 일은 그렇게 많지 않다.



3. 잠금 고려사항

1) BLOCKING 시나리오

잠금에 대한 BLOCKING 문제의 대부분은 단일 프로세스가 오래도록 잠금을 유지하여 다른 모든 프로세스가 잠금 대기 상태로 되는 블로킹된 프로세스 체인을 초래하기 때문에 발생한다.
일반적인 BLOCKING 시나리오는 다음과 같다.

1] 실행 시간이 긴 쿼리 전송
실행 시간이 긴 쿼리는 다른 쿼리를 BLOCKING 할 가능성이 크다. 예를 들어, 많은 행에 영향을 주는 DELETE, UPDATE 작업에서는 테이블 전체에 대한 잠금과 상관없이, 다른 쿼리를 BLOCKING 하는 잠금을 많이 설정할 수 있다. 따라서, 일반적으로 실행 시간이 긴 OLAP 용 질의를 OLTP 와 동일한 데이터베이스나 서버에서 사용하는 것은 결코 권장하지 않는다. 물론 대용량 데이터베이스에서는 이런 일이 없을뿐더러, 중소규모 데이터베이스에서도 이런 일은 발생하지 않아야 한다.
이에 대한 해결 방법은 인덱스를 변경하거나, 크고 복잡한 쿼리를 간단한 쿼리로 분할하거나, 한가한 시간에 또는 별도의 컴퓨터에서 쿼리를 실행하여 쿼리를 최적화할 수 있는 방법을 찾는 것이 최고다.

쿼리가 오래도록 실행되어 BLOCKING 을 초래하게 되는 한 가지 이유는 커서를 잘못 사용하는 경우를 들 수 있다. 커서는 결과 집합을 편리하게 탐색할 수 있는 방법이지만, 커서를 사용하게 되면 레코드셋 지향의 쿼리보다 속도가 느려질 가능성이 크다.

2] COMMIT /ROLLBACK 되지 않은 쿼리를 취소하는 경우 응용 프로그램에서 쿼리를 취소하는 경우, 예를 들면 ROLLBACK과 COMMIT 문을 필요한 횟수만큼 실행하지 않고서 ODBC sqlcancel 함수를 사용하는 경우 발생할 수 있다.
쿼리를 취소하면 트랜잭션이 자동으로 롤백되거나 커밋되지 않는다. 따라서 트랜잭션 안에서 얻은 모든 잠금은 쿼리가 취소된 후에도 유지된다. 이런 상황이라면, 응용 프로그램에서는 취소된 트랜잭션을 커밋하거나 롤백함으로써, 트랜잭션 중첩 수준을 적절히 관리해야 할 것이다.

3] 모든 결과 처리를 완료하지 않는 응용 프로그램
응용프로그램에서 특정 쿼리를 DB 서버로 보낸 후, 모든 응용 프로그램에서는 모든 결과 행을 완료하기
위해 즉시 반입해야 한다. 응용 프로그램에서 모든 결과 행을 반입하지 않으면 테이블에 잠금이 남게 되어 다른 사용자를 BLOCKING 하게 될 수도 있다. Transact-SQL 문을 서버로 투명하게 전송하는 응용 프로그램을 사용하는 경우에는 응용 프로그램에서 모든 결과 행을 반입해야 한다. 그렇지 않은 경우 및 그렇게 구성할 수 없는 경우에는 BLOCKING 문제를 해결하지 못할 수도 있다.
이러한 응용 프로그램을 보고 또는 의사 결정 지원 데이터베이스로 제한하면 문제를 피할 수 있다.

4] 분산 클라이언트/서버 교착 상태
기본 교착 상태와 달리, 안타깝게도 SQL Server 2000은 분산 교착 상태를 자동으로 검색할 수 없다.
분산 클라이언트/서버 교착 상태는 응용 프로그램에서 SQL Server에 대해 여러 개의 연결을 열고 쿼리를 비동기식으로 전송하는 경우에 발생할 수 있다.

예를 들어, 단일 클라이언트 응용 프로그램 스레드에 두 개의 연결이 열려 있고, 비동기식으로 트랜잭션을 시작하며 첫 번째 연결에 대해 쿼리를 실행한다. 그런 다음, 응용 프로그램에서 다른 트랜잭션을 시작하고, 또 하나의 연결에 대해 쿼리를 실행하고, 결과를 기다린다. SQL Server에서 한 개의 연결에 대한 결과를 반환하면 응용 프로그램이 결과를 처리하기 시작한다. 결과를 생성하는 쿼리는 다른 연결에 대해 실행되는 쿼리에 의해 BLOCKING 되므로 응용 프로그램은 더 이상 사용 가능한 결과가 없을 때까지 결과를 처리한다.
이 시점에서 첫 번째 연결이 BLOCKING 되어 처리할 결과를 무한정 기다리게 된다. 두 번째 연결은 잠금 상태로 BLOCKING 되지는 않지만, 응용 프로그램에게 결과 반환을 시도한다.
그러나 응용 프로그램이 BLOCKING 되어 첫 번째 연결에 대한 결과를 기다리므로, 두 번째 연결에 대한
결과는 처리되지 않는다.
다음 중 하나를 사용하여 이 문제를 방지할 수 있다.

- 각 쿼리에 대한 쿼리 제한 시간
- 각 쿼리에 대한 잠금 제한 시간.
- 바운드 연결.

SQL Server는 기본적으로 클라이언트 응용 프로그램의 명령에 따라 움직인다. 서버에서 얻는 잠금에
대해서는 클라이언트 응용 프로그램이 거의 모든 제어권(및 책임)을 가진다. SQL Server 잠금 관리자는
잠금을 사용하여 자동으로 트랜잭션을 보호하지만, 이것은 클라이언트 응용 프로그램이 보낸 쿼리 유형과 결과가 처리되는 방식에 의해 직접 이루어진다. 따라서, BLOCKING 문제를 해결할 때는 대부분의 경우 클라이언트 응용 프로그램 검사가 수반된다.

BLOCKING 문제를 해결하기 위해서는 응용 프로그램이 전송하는 SQL 문과 연결 관리, 모든 결과 행 처리 등에 관련된 응용 프로그램의 동작이 모두 정확한지 검사해야 하는 경우가 많다.
개발 도구에서 연결 관리, 쿼리 제한 시간, 결과 처리 등에 대한 명시적인 제어가 허용되지 않으면 BLOCKING 문제는 해결되지 않을 수도 있다.


2) 블로킹을 방지하기 위한 응용 프로그램 디자인 지침

1] 실행 시간이 긴 쿼리가 생성될 수 있는 응용 프로그램은 사용하거나 디자인하지 않는 것이 좋다.
예를 들어, 응용프로그램에서 특정 필드를 공백으로 두거나, 와일드카드를 입력할 수 있게 허용하지 않고 사용자에게 입력을 요구하는 응용 프로그램은, 사용하거나 디자인하지 말자. 이렇게 하면 응용 프로그램에서 실행 시간이 너무 긴 쿼리를 전송하게 되어 BLOCKING 문제를 초래할 수 있기 때문이다.

2] 트랜잭션 내에서 사용자 입력을 허용하는 응용 프로그램을 사용하거나 디자인하지 않는다.

3] 쿼리 취소를 허용하게 한다.

4] 쿼리 또는 잠금 제한 시간을 사용하여 쿼리가 무한정 실행되지 않게 하며, 분산 교착 상태를 방지한다.
5] 모든 결과 집합을 즉시 반입하여 완료한다.

6] 트랜잭션을 되도록 간략하게 유지한다.

7] 연결 관리를 명시적으로 제어한다.

8] 예상되는 전체 동시 사용자 로드로 응용 프로그램의 문제 상황을 테스트한다.



4. DEADLOCK 방지

1) 교착 상태(DEADLOCK) 개요

교착상태 란, 둘 이상의 스레드 간에 특정 자원(테이블,행..) 대한 양방향 참조(종속) 관계가 발생한 경우를 말한다. 교착 상태는 주로 단순한 RDBMS 보다는, MULTI THREAD 가 가능한 시스템에서 발생할 가능성이 더 크다.
특정 트랜잭션의 스레드는 하나의 이상의 자원에 잠금을 얻을 수 있다. 지금까지 보았던 것처럼 잠겨진
자원을 다른 트랜잭션의 스레드가 엑세스하려 한다면, 대상 리소스가 해제될 때까지 기다려야 하는 BLOCKING 이 발생한다. 이 때 대기 중인 스레드는 리소스를 소유한 스레드에 대해 해당 리소스에 대한
종속 관계를 갖고 있다고 말한다.

예를 들어, 트랜잭션 1을 실행하는 스레드 T1에는 EMP 테이블에 대한 배타적 잠금이 있고, 트랜잭션 2를 실행하는 스레드 T2는 CUST 테이블에 대한 배타적 잠금이 있다.
이때 T2는 EMP에 대해 잠금을 요청할 수 있다. 그러나 T1이 현재 배타적 잠금을 가지고 있으므로 BLOCKING 이 발생한다. T2는 차단되고 T1이 완료되기를 기다릴 수밖에 없는 상황이다.
이것은 일반적인 BLOCKING 이다.
그러나, 이런 상황에서 T1이 CUST 테이블에 잠금을 요청하려 하면, 역시 T2가 잠궈 놓았으므로
BLOCKING 이 발생한다. 일반적인 BLOCKING 이라면, 잠금을 건 해당 트랜잭션이 COMMIT/ROLLBACK 전까지만 기다리면 된다. 하지만 지금은 일반적인 BLOCKING 이 아니다.
이런 때는 서로 잠금을 해제할 수 없으며, 또한 커밋하거나 롤백할 수도 없다.
이렇게 서로 잠금을 소유하고 있는 두 트랜잭션의 스레드에서, 상대방이 소유한 자원을 원하는 경우에는
교착 상태가 발생한다. 두 스레드 모두 트랜잭션을 커밋하거나 롤백할 때까지 자신이 소유한 리소스를 해제할 수 없고, 또 다른 트랜잭션이 소유한 리소스를 대기하고 있으므로 트랜잭션을 커밋하거나 롤백할 수도 없다.

<SESSION1>
BEGIN TRAN
UPDATE EMP
SET SAL = 40000
WHERE EID = 1
DBCC OPENTRAN
EXEC SP_LOCK 51

실행한 SP_LOCK명령에 의해, 현재 해당 키인 EID 1번에 대해 X 락이 걸려 있는 것이 확인된다.

<SESSION2>
BEGIN TRAN
UPDATE CUST
SET CTEL = '032-333-9999'
WHERE CID = 2
DBCC OPENTRAN
EXEC SP_LOCK 52

역시 키인 CID 1번에 대해 X 락이 걸려 있다.
이번엔 첫 번째 트랜잭션에서 두 번째 트랜잭션에서 배타적 잠금을 설정한 CID 1번을 수정시도 해보자.
SQL Server 가 잠금을 요청할 것이나, 현재 배타적 잠금이 벌써 두 번째 트랜잭션에 의해 잠금이 설정된 상태이므로 BLOCKING 이 발생한다. 이것은 일반적인 BLOCKING 이다.
그러므로 계속 대기상태에 접어든다.

<SESSION1>
BEGIN TRAN
UPDATE EMP
SET SAL = 40000
WHERE EID = 1
DBCC OPENTRAN
EXEC SP_LOCK 51
DELETE CUST
WHERE CID = 2


SELECT @@TRANCOUNT
ROLLBACK TRAN

현재 BLOCKING 이 설정되어 있다.
아래의 쿼리를 보면, 이번에는 두 번째 트랜잭션에서 다시 첫 번째 트랜잭션에서 배타적 잠금을 걸고 있는 자원인 EMP 테이블의 EID 값에 대해서 삭제작업을 시도해보자. 또 BLOCKING 이 발생한다.
그러나 이번에는 DEADLOCK 이 발생하게 되고, 그리고 두 트랜잭션 중 하나만 계속 진행 중인 상태로 남고 나머지 하나는 자동으로 ROLLBACK 된다.

<SESSION2>
BEGIN TRAN
UPDATE CUST
SET CTEL = '032-333-9999'
WHERE CID = 2
DBCC OPENTRAN
EXEC SP_LOCK 52
DELETE EMP
WHERE EID = 1
ROLLBACK

위에서 보여주는 바로는 두 번째 트랜잭션이 에러번호 1205번을 출력하고, 취소되었다.
그리고 첫 번째 트랜잭션은 여전히 진행 중이다.

<SESSION1>
BEGIN TRAN
UPDATE EMP
SET SAL = 40000
WHERE EID = 1
DBCC OPENTRAN
EXEC SP_LOCK 51
DELETE CUST
WHERE CID = 2

SELECT @@TRANCOUNT
ROLLBACK TRAN

이렇게 BLOCKING 상태가 계속 진행되지 않고 하나의 트랜잭션이 바로 취소되는 이유는, DEADLOCK 상태를 취소하지 않으면 두 트랜잭션 모두 자신의 잠금을 풀 수 없기 때문에 잠금상태가 영원히 지속되기 때문이다. DEADLOCK 상태는 트랜잭션이 스스로 잠금을 풀 수 없으므로, SQL Server 가 자동으로 둘 중 하나의 잠금을 풀고 트랜잭션을 취소시키는 것이다.

여러분들이 해야할 일은 이렇게 DEADLOCK 일어나 트랜잭션 중 하나가 취소된 것을 감지하고 해당 트랜잭션을 다시 실행하거나 취소하는 처리이다. DEADLOCK 감지는 @@ERROR 1204번으로 한다.
어떤 트랜잭션에서 @@ERROR 1205번 에러가 발생했다면 데드락이 발생했다는 것이고, 해당 트랜잭션을 제어해주는 로직을 여러분들이 해당 트랜잭션 내에 추가해야 하는 것이다.

/* 참고
교착 상태는 종종 일반적인 BLOCKING 과 혼동된다. 한 트랜잭션이 다른 트랜잭션이 원하는 리소스를 잠그고 있으면 두 번째 트랜잭션이 해당 잠금이 해제되기를 기다린다. 기본적으로, LOCK_TIMEOUT이 설정되지 않는 한, SQL Server 트랜잭션 시간은 제한되지 않는다.
이 경우 두 번째 트랜잭션이 차단되지만 교착 상태는 아니다.
*/


4) 교착상태 분석

추적 플래그 1204 사용

이것은 명령프로프트에서 실행하는 명령어이다. SQL Server 시작 옵션 중의 하나인 /T 옵션(대문자)를
사용해서 SQL Server를 시작하게 되면 볼 수 있다.
교착 상태에서 이 추적 플래그 1204는 대기 상태의 스레드, 이 스레드가 대기하고 있는 리소스, 리소스 간의 종속 관계 주기를 나타낸다.

추적 플래그 1204 보고서 용어

추적 정보 1204는 관련된 리소스에 따라 다른 정보를 반환하지만 일반적으로 보고서에는 다음과 같은 용어가 포함된다 .

- Node:x
교착 상태 체인에서 항목 번호(x)를 표시한다.

- Lists
잠금 소유자가 다음 Grant, Convert 및 Wait 목록의 일부일 수 있다.

- Grant List
리소스의 현재 소유자를 열거한다.

- Convert List
잠금을 더 높은 수준으로 변환하려는 현재 소유자를 열거한다.

- Wait List
리소스에 대한 현재 새 잠금 요청을 열거한다.

- SPID: x ECID: x
병렬 프로세스의 경우 시스템 프로세스 ID 스레드를 확인한다. 항목 SPID x ECID 0은 주 스레드를 나타내며 항목 SPID x ECID > 0 은 같은 SPID에 대한 하위 스레드를 나타낸다.

- Statement Type
SELECT, INSERT, UPDATE 또는 DELETE 문 등이며, 스레드는 이에 대해 사용 권한을 갖는다.

-Line #
현재 명령문 배치에 있는 줄을 나열하는 데, 이 줄은 교착 상태가 발생할 때 실행된다.

- Input Buf
현재 배치에 있는 모든 명령문을 나열한다.

- Mode
스레드가 요청하고 허용하고 또는 대기하는 특정 리소스에 대한 잠금 유형을 보여준다.
모드는 IS(Intent Shared), S(Shared), U(Update), IX(Intent exclusive), SIX(Shared with intent exclusive) 및 X(Exclusive) 등이다.

- RID
잠금이 걸려 있거나 요구되는 테이블 안의 단일 행을 확인한다.

- RID는 추적 플래그 1204에서 RID: db_id:file_id:page_no:row_no로 표시된다.
예를 들면, RID: 1:1:1253:0입니다.

- TAB
잠금이 걸려 있거나 요구되는 테이블을 확인한다. TAB은 추적 플래그 1204에서 db_id:object_id로 표시됩니다.
예를 들면 TAB:2:2009058193 이다.

- KEY
잠금이 걸려 있거나 요구되는 인덱스 안의 키 범위를 확인한다. KEY는 추적 플래그 1204에서
KEY: db_id:object_id:index_id로 나타난다. 예를 들면, KEY: 2:1977058079:1 이다.

- PAG
잠금이 걸려 있거나 요구되는 페이지 리소스를 확인한다. PAG는 추적 플래그 1204에서
PAG: db_id:file_id:page_no로 나타난다. 예를 들면, PAG: 7:1:168 이다.

- EXT
익스텐트 구조를 확인한다. EXT는 추적 플래그 1204에서 EXT: db_id:file_id:extent_no로 나타난다.
예를 들면, EXT: 7:1:9이다.

- DB
데이터베이스 잠금을 확인한다. DB는 추적 플래그 1204에서 다음 방법 중 하나로 나타난다.
DB: db_id
DB: db_id[BULK-OP-DB], 이것은 백업 데이터베이스가 갖는 데이터베이스 잠금을 확인한다.
DB: db_id[BULK-OP-LOG], 이것은 특정 데이터베이스에 대해 백업 로그가 갖는 잠금을 확인한다.

- IND
인덱스 리소스에서 만들어진 인덱스가 갖는 잠금을 확인한다. IND는 추적 플래그 1204에서 다음 방법 중 하나로 나타난다.
IND: db_id:object_id:index_id
IND: db_id:object_id:index_id[INDEX_ID], 이것은 인덱스 ID가 잠겼음을 나타난다.
IND: db_id:object_id:index_id[INDEX_NAME], 이것은 인덱스 이름이 잠겼음을 나타난다.

- APP
응용 프로그램 리소스가 갖는 잠김을 확인한다. APP는 추적 플래그 1204에서 APP: lock_resource로
나타난다. 예를 들면, APP: Formf370f478 이다.

SQL Server가 응용 프로그램 리소스를 교착 상태에서 처리하지 않으면, 해당 응용 프로그램 리소스 소유자는 앞에서 설명한 오류 메시지를 받지 않는다.
대신에 이 응용 프로그램은 sp_getapplock 저장 프로시저가 해당 응용 프로그램 리소스에서 실행될 때
"-3" 반환 코드를 받환받는다.

- Victim Resource Owner
교착 상태 주기를 끊기 위해 SQL Server가 처리하지 않는 진행 중인 스레드를 보여준다.
선택된 스레드(SPID x ECID 0로 확인)와 기존의 모든 하위 스레드(SPID x ECID > 0로 확인)는 제거된다 .

- Next Branch
교착 상태 주기에 관련된 동일한 SPID에서 두 개 이상의 하위 스레드를 나타낸다.

교착 상태가 병렬 처리와 관련이 있으면 여러 하위 스레드가 통신 버퍼에서 차단될 수 있으며, 스레드 한 개는 다른 하위 스레드에 대해 대기 상태로 된다. 다른 모든 스레드가 교착 상태와 관련이 있는 경우에만 교착 상태 상황이다. Next Branch는 대체 경로를 추적하는 교착 상태 주기를 나타낸다.


/* 참고
일반적으로 SQL Server는 실행을 취소했을 때 가장 손해가 적은 트랜잭션을 실행하는 스레드를 교착 상태 희생자로 선택한다. 또는 사용자가 SET 문을 사용하여 세션의 DEADLOCK_PRIORITY를 LOW로 설정할 수 있다. DEADLOCK_PRIORITY 옵션은 교착 상태 상황에서 세션의 중요도를 판단하는 방법을 제어한다. 세션이 LOW로 설정되어 있으면 교착 상태가 발생했을 때 해당 세션이 희생자로 선택된다.
*/

SET DEADLOCK_PRIORITY
이 명령은 교착 상태에 있을 때 세션이 반응하는 방법을 제어한다. 교착 상태는 두 프로세스에 잠긴 데이터가 있고, 다른 프로세스가 그 잠금을 해제할 때까지 자신의 잠금을 해제하지 않을 때 발생한다.



3) 교착상태 해결
교착상태가 걸린 저장 프로시저나 일괄처리 내에서는 교착상태에 대한 오류처리를 할 수 없다.
해당 처리가 종료된 후 따로 처리해 주어야 한다.

<SESSION2>
CREATE PROC UP_LOCKTEST
AS
BEGIN TRAN
UPDATE CUST
SET CTEL = '032-333-9999'
WHERE CID = 2
--DBCC OPENTRAN
--EXEC SP_LOCK 52
DELETE EMP
WHERE EID = 1
COMMIT TRAN
GO

IF @@ERROR = 1205
BEGIN
EXEC UP_LOCKTEST
SELECT 'OK'
END


4) 교착상태 최소화
교착 상태를 완전히 피할 수는 없다. 그러나 교착 상태 수를 최소화할 수는 있다.
교착 상태를 최소화하면 트랜잭션 처리량이 늘어나고 트랜잭션 수가 적어지기 때문에 시스템 오버헤드가 줄어든다.

1] 같은 순서로 개체에 액세스하게 한다.
모든 동시 트랜잭션이 같은 순서로 개체에 액세스하면 교착 상태가 일어날 가능성이 줄어든다.
예를 들어, 두 개의 동시 트랜잭션이 EMP 테이블에 대해 잠금을 얻은 다음 CUST 테이블에 대해 잠금을 얻으면, 다른 트랜잭션이 완료될 때까지 한 트랜잭션이 EMP 테이블에서 BLOCKING 된다.
첫 번째 트랜잭션이 COMMIT /ROLLBACK 하면 두 번째 트랜잭션은 계속 진행된다.
그리고 교착 상태는 발생하지 않게 된다. 모든 데이터 수정에 대해 저장 프로시저를 사용하는 것은, 개체 액세스 순서를 표준화할 수 있는 방법이 된다.

2] 트랜잭션 중 사용자 상호 작용을 피한다.
사용자 간섭 없이 실행 중인 일괄 처리의 속도는, 응용 프로그램에서 요청한 매개 변수에 대한 프롬프트에 응답하는 등 사용자가 직접 쿼리에 응답하는 속도에 비해 매우 빠르므로 사용자 상호 작용이 필요하도록 트랜잭션을 작성하지 않는 것이 좋다.
예를 들어, 트랜잭션이 사용자 입력을 기다리고 있는데 사용자가 식사를 하러 가거나 퇴근한 경우 사용자는 트랜잭션을 완료할 수 없다. 트랜잭션이 소유한 잠금은 트랜잭션이 COMMIT /ROLLBACK 될 때만 해제되므로 이렇게 하면 시스템 처리량이 현저히 줄어들게 된다.
교착 상태가 발생하지 않아도 같은 리소스에 액세스하는 다른 트랜잭션이 차단되므로 트랜잭션이 완료되려면 기다려야 하기 때문이다.

3] 트랜잭션을 하나의 일괄 처리로 짧게 유지한다.
교착 상태는 보통 여러 개의 긴 트랜잭션이 같은 데이터베이스에서 동시에 실행될 때 발생한다.
트랜잭션 실행 시간이 길수록 배타적 또는 업데이트 잠금 시간이 길어지고 다른 작업을 차단하여 교착 상태가 발생할 가능성이 높아진다.
그러나, 트랜잭션을 하나의 일괄 처리로 유지하면 트랜잭션 중 네트워크 왕복이 최소화되므로 트랜잭션을 완료하고 잠금을 해제하는 데 걸리는 시간을 줄일 수 있다.

4] 낮은 TRANSACTION_ISOLATION 레벨을 사용한다.
트랜잭션을 더 낮은 격리 수준에서 실행할 수 있는지 확인해 본다. READ UNCOMMITTED 를 구현하면
첫 번째 트랜잭션이 완료될 때까지 기다리지 않고 이전에 읽은(수정하지 않은) 데이터를 읽을 수 있다.
READ UNCOMMITTED 등 낮은 잠금 수준을 사용하면 SERIALIZABLE 등의 높은 잠금 수준보다 짧은 기간 동안 공유 잠금을 보유하므로 잠금 경쟁률이 줄어든다.

5] 바운드 연결을 사용한다.
바운드 연결을 사용하면 같은 응용 프로그램에서 열고 있는 둘 이상의 연결을 함께 사용할 수 있다.
두 번째 연결에서 얻은 잠금은 주 연결에서 얻은 것처럼 보유되며 그 반대의 경우도 마찬가지로 처리된다.
따라서 서로를 BLOCKING 하지 않는다.


'MSSQL' 카테고리의 다른 글

(펌) MSSQL Server 백업 / 복구 시나리오  (0) 2011.10.20
(펌) MSSQL 2005 백업계획작성  (0) 2011.10.20
(펌) MSSQL lock & blocking  (0) 2010.12.07
(펌) MSSQL2005 - New isolation level  (0) 2010.12.07
(펌) MSSQL SNAPSHOT 격리 수준  (0) 2010.12.07





출처 : http://www.devpia.co.kr/Maeul/Contents/Detail.aspx?BoardID=42&MAEULNO=17&no=40&page=3




원문 : [SQL Server 2005에서 좋아진 점 No.1] - New isolation level








1. 시작하기 전에

 

파일 시스템과 데이터베이스 시스템과의 차이점은 어떤 것들이 있을까? 여러 가지가 있겠지만,

 이번 강좌에서 다뤄볼 것은 트랜잭션(Transaction)과 동시성(Concurrency)에 관한 것들이다.

이 두가지 개념으로 인하여 많은 상황이 발생한다. 예를 들자면, 이런 것들이다.

 

 - 한 사용자가 데이터를 읽어나가고 있는데, 다른 사용자가 그 데이터를 변경하려 한다면?

 - 한 사용자가 데이터 변경작업을 해나가고 있는데, 다른 사용자가 그 데이터를 읽으려

   한다면 변경중인 데이터를 읽어야 할까? 아님 변경이 읽어나기전의 데이터를 읽어야 할까?

 

이런 상황들에 대해서 어느 정도 수준까지 동시성을 제공해줄 수 있는 가를 정의한 것이

격리수준(Isolation level)이라는 것이다. 어느 정도의 격리수준을 제공하는 가에 따라서

동시성이 높아질 수도 낮아질 수도 있다.

 

개인적으로 SQL Server는 중소형 규모에 적절한 시스템이라 생각한다. 왜 그렇게 생각하는가 하면,

SQL Server가 제공하는 격리수준이 상대적으로 타사의 제품에 비해서 떨어지는 편이기 때문이다.

SQL Server의 기본적인 동시 사용 제어(Concurrency Control)는 비관적 동시 사용 제어 개념을

사용한다. 이 말은 쓰기 작업이 읽기 작업에 영향을 줄 수 있고, 그 반대로 읽기 작업이 쓰기 작업에

영향을 줄 수 있기 때문에 하나의 자원에 대해서 한 가지 형태의 작업이 이뤄지고 있으면 다른 형태의

작업이 접근하지 못하도도록 자원을 잠궈버린다.(Locking)

이 때에 다른 작업은 선행 작업이 끝날때 까지 그 자원에 접근을 하지 못하며 대기 상태가 된다.(Blocking)

 

근데 이 정도에서 상황이 종료되는 것이 아니다. 기본적으로는 row 단위의 잠금을 하지만,

자원에 대해서 잠금 현상이 점점 많아지게 되면 SQL Server는 잠금에 사용되는 자원를 보다 손쉽게

관리하기 위해서 잠금의 단계를 높여버리게 된다.

예를 들어서 row단위의 잠금을 table단위로 올리는 것이다. 이것을 lock escalation 이라는 말로 표현한다.

잠금의 사용되는 자원을 보다 손쉽게 관리한다는 측면 - SQL Server의 입장에서는 맞는 말일수도 있겠지만,

사용자 측면에서는 잠금 현상의 확대로 인해서 데드 락이 발생하여 무한 대기 상태 또는 세션의 강제 종료 등과

같은 별로 달갑지 않는 현상이 발생해 버린다.

 

이런 현상을 해결하기 위해서 SQL Server 2000 버전까지는 각 트랜잭션 단위를 빠르고 신속하게 처리할 수 있도록

Query를 튜닝하거나 with nolock 같은 힌트를 이용하는 등을 이용하여 격리수준을 낮춰버리는 등의 작업으로 해결하곤 했다.

 

각 트랜잭션 단위가 빠르게 수행되도록 하는 Query 튜닝은 어떤 DBMS 제품을 이용하더라도 필수적인 일이겠으나,

운영중인 시스템들은 두 서너개의 Query만을 가지고 있는 것이 아니라, 몇 백, 몇 천개가 넘어가는 Query를 가지는

경우가 대부분이기 때문에 그런 Query들을 튜닝한다는 말은 그리 쉬운 일이 아니다.

그리고 격리 수준을 낮춘다는 말은 데이터의 일관성(consistency)이 깨져버린 믿지 못할 데이터를 이용하고 있다는

말 밖에는 되지 않는다. 자신들이 이용하는 데이터가 신뢰성이 결여된 것을 고객들이 알게 된다면 어떤 일이 벌어질까?

 

SQL Server 2005에서는 이런 동시성을 높여줄 새로운 Isolation level이 추가 되었다.

사용방법은 정말 간단하다. 옵션의 플래그 값만을 변경해주면 가능하다.

하지만, 그 간단한 작업의 뒷편에는 개념에 대한 충분한 이해와 새로운 이 기능을 적용하기 위해서 고려해야할 사항들이 있다.

그냥 막연하게 새로운 기능이니 좋겠지, 한번 해볼까? 하는 생각만으로 옵션을 변경하지는 말자.

 

 

2. 개념

 

앞서 장황하게 설명했던 새로이 추가된 격리 수준은 SNAPSHOT ISOLATION이다.

이 격리수준은 row versioning이라는 기술을 기반으로 구현된 것이다. 각 row 단위에 14byte의 unique한 식별자를

부여한다. 이 식별자를 기반으로 버전 관리를 하여서 읽기/쓰기 작업에 대한 요청이 들어올 때에 작업에 적절한

버전을 반환하여 해당 버전에 대한 데이타를 작업에 사용하게 하도록 한다.

 

SNAPSHOT이라는 말에서도 느낌이 오듯이 해당 버전에 대한 데이타의 복사본을 만들어서 요청에

응답하기 때문에 앞서 말한 BLOCKING이나 일관성(CONSISTENT)이 깨진 데이타를 제공하지는 않는다.

이 때문에 잠금 현상이 줄어들어 동시성을 높여줄 수가 있게 되는 것이다.

 

예를 들어서 설명을 하자면, 이러하다.

VER 1.0에 대한 데이타를 읽기 트랜잭션이 시작되어서 읽어나가고 있다고 하자.

이 때에 쓰기 트랜잭션이 시작되었다면, 블록킹 현상없이 해당 데이터의 버전을

VER 1.1로 기록하고 작업을 해나가면 된다.

 

또, 쓰기 트랜잭션이 계속되고 있는 상황에서 읽기 트랜잭션이 시작되었다면,

아직 COMMIT이 되지 않아서 반영이 되지 않은 데이타이므로, VER 1.0의 데이타를

블록킹 없이 읽어나가면 된다. 그리고, COMMIT이 된 후에 시작된 읽기

트랜잭션이라면 VER 1.1의 데이타를 읽어가게 될 것이다.

역시 쓰기 작업이 진행되고 있는 상태에서 읽기 작업이 시작되었지만, 블록킹 현상없이 작업이 처리가 될 것이다.

 

좀 더 세부적으로 알아보자. 크게 두가지 형태로 SNAPSHOT이 제공된다.

 

SNAPSHOT isolation level

READ COMMITTED SNAPSHOT isolation level

 

이 두 격리수준의 차이점은 어느정도 수준까지 적용을 해줄 것인가에 관한 것이다.

먼저 SNAPSHOT isolation level은 트랜잭션 단위까지 위의 기술을 제공한다.

두번째로 READ COMMITTED SNAPSHOT ISOLATION LEVEL은 QUERY 단위까지 지원을 한다.

 

먼저 SNAPSHOT isolation level에서의 예를 들어보면 이렇다.

하나의 트랜잭션이 시작되고 읽기 작업이 시작되었다. VER 1.0의 데이타를 반환하였다.

그리고 중간에 다른 쓰기 트랜잭션이 시작되어 데이타를 변경해버렸다.

그 이후에 첫번째 트랜잭션이 다시 데이타를 읽는 다면 VER 1.0의 데이타를 반환하게 된다.

 

두번째 READ COMMITTED SNAPSHOT ISOLATION LEVEL은 조금 다르다.

첫번째 커넥션에서 읽기 작업을 했더니 VER 1.0의 데이타를 반환하였다.

중간에 다른 커넥션에서 해당 데이터를 VER 1.1로 반환하였다.

첫번째 커넥션에서 다시 읽기 작업을 하게 되면 VER 1.1로 반환을 하게 된다.

말그대로 작업 시점에 COMMITTED가 된 DATA를 반환하게 되는 것이다. 물론 잠금이나 블록킹 없이 말이다.

 

근데, 여기서 SNAPSHOT ISOLATION LEVEL에 대해 좀 더 생각하다보면 이런 상황이 발생할 수 있다.

첫번째 트랜잭션에서 읽기 작업이 이뤄져 VER 1.0의 데이타를 반환하였다.

그런데, 첫번째 트랜잭션에서 읽기 작업이 이뤄지는 중에 다른 트랜잭션에서 데이타를 VER 1.1로 변경한 후

COMMIT까지 이뤄져버렸다. 그 후에 첫번째 트랜잭션에서 읽기 작업으로 얻은 데이타를 쓰기 작업을

하려 한다면 어떻게 될까?

VER 1.2가 기록이 되면서 정리가 될까? 아니다.

에러가 나면서 트랜잭션은 롤백처리가 되어버린다. 아주 당연한 일이겠지만, 해당 데이타에 갱신을

하게되는데에 잘못된 데이타 버전을 기반으로 작업을 하려 한 것을 갱신하려고 한 것이기 때문에

데이타의 갱신이 이뤄지면 않된다.

그런 부분까지 꼼꼼히 챙기고 있기 때문에 DATA 일관성에는 별 문제가 없다.

 

 

3. 고려해야 할 점

 

여기까지 읽어본 느낌이 어떠한가? 어라~ 많이 좋아졌네? 이 좋은 기능을 왜 진작에 제공을 하지 않았는가?

등등의 여러 느낌이 있으리라 생각된다.

 

앞서 이런 말을 했다. 그냥 좋은가보다라는 생각으로 이 옵션을 적용하지 말라고~

새로 제공되는 isolation level은 잠금 현상을 줄여서 동시 사용성을 높여주지만, 대신에 나빠지는 것도 있다.

 

각 데이터의 버전에 대한 SNAPSHOT을 만든다는 것을 기억하는가?

SQL Sever에서는 row versiong을 위한 data snapshot을 tempdb database에 만들어서 관리한다.

때문에 이 기능을 사용하게 되면 tempdb database에 I/O이 늘어날 수 밖에 없다.

다른쪽에 LOCKING으로 발생하는 병목현상은 해결했지만, TEMPDB DATABASE에 대한 병목현상이

새로이 생겨버리게 된다. 때문에 TEMPDB DATABASE에 대한 I/O 증가로 인한 병목현상에 대한

고려가 있어야 한다.

TEMPDB DATABASE에 대한 확장 옵션, TEMPDB DATABASE가 저장되어 잇는 디스크 시스템 등에

대한 점검과 고려를 충분히 해보고, 테스트를 충분히 해본 다음에 적용을 하길 바란다.

 

이 옵션은 DEFAULT가 아니다. 이런 부분들에 대한 고려를 해본 다음에 적용하라는 사연이

있어서 그런 것이 아닌가하고 생각해본다. 장점만 있는 기능이라면 DEFAULT가 OFF는 아닐테니 말이다.

 

 

4. 적용하기

 

드디어 적용하기에 돌입했다. 앞서 말한 것처럼 장황하게 부연설명한 것에 비해선 허무할 정도로 간단하다.

 

먼저 SQL Server에 있는 데이타베이스들에 해당 옵션이 적용되어 있는지를 살펴보자.

아래의 쿼리를 쿼리창을 열어서 실행시켜보자.

 

SELECT NAME, SNAPSHOT_ISOLATION_STATE,

SNAPSHOT_ISOLATION_STATE_DESC, IS_READ_COMMITTED_SNAPSHOT_ON

FROM SYS.DATABASES

 

SNAPSHOT_ISOLATION_STATE, IS_READ_COMMITTED_SNAPSHOT_ON의 값이 1이면

적용이 되고 있다는 것이고, 0은 적용이 되고 있지 않다는 뜻이다.

 

SNAPSHOT ISOLATION LEVEL를 적용하기 위해선 아래의 쿼리를 실행하면 된다.

 

ALTER DATABASE 데이터베이스명 SET ALLOW_SNAPSHOT_ISOLATION ON

 

당연히 기능을 정지하려고 할 때에는 OFF로 플래그만 변경해서 실행하면 된다.

 

 

READ COMMITTED ISOLATION LEVEL를 적용하기 위해선 아래의 쿼리를 실행한다.

 

ALTER DATABASE 데이터베이스명 SET READ_COMMITTED_SNAPSHOT ON

 

역시 해당 기능을 정지하려고 할 때에는 OFF로 플래그만 변경하면 된다.

 

위 기능을 적용하기 위해서는 데이터베이스에 접속되어 있는 사용자가 없어야한다.

 

 

5. 맺음말

 

이 강좌에서는 개념적인 설명이 많았다. 적용하는 명령은 솔직히 한 줄로도 끝난다.

하지만, 제대로 사용하기 위해서는 개념에 대한 이해가 있어야 하기 때문이다.

 

이전 버전에 비해선 장족의 발전(?)이라 생각을 한다. 하지만, 아직 완전한 기능(?)은

아닌 것 같다는게 개인적인 느낌이다.

이전 버전과의 호환성 등을 이유로 Default가 OFF인 형태로 제공된듯 하지만,

고려할 점에서 언급한 것처럼 I/O에 대한 추가적인 부하가 있기 때문이다.

 

그리고 여기서 언급한 isolation 레벨은 오라클에서는 예전부터 제공이 되고 있던 기능이다.

개인적인 느낌으론 TPC에서 나오는 SQL서버 관련 수치들은 앞서 언급한 기능들을 배제하고

만들어낸, 차떼고 포떼고 해서 만들어낸 수치일수도 있다.

 

그렇다면, 오라클만 좋고 SQL서버는 나쁜 것일까?

 회색분자 같이 들릴 수도 있지만, 개인적으론 오라클과 SQL서버 모두 좋아한다.

각각의 DBMS가 주는 장단점이 다르다고 생각하기 때문이다.

정원혁님의 마이크로소프트웨어 기사에서처럼 현시점에선 비용기반으론 SQL 서버가 오라클보다

우위에 있기 때문이다. 비용을 고려하지 않고 프로젝트를 진행하는 곳이 얼마나 될까?

프로젝트를 진행한다는 말은 결국 비용과의 싸움이니까 말이다.

 

장인은 연장을 탓하지 않는 다는 말이 있다.

최고의 성능을 가진 오라클에서도 엉망진창으로 설계와 코딩을 진행하면 무지하게 느려질 수도 있다.

SQL 서버에서도 제품이 가진 특성을 잘 고려해서 설계와 운영을 해나간다면,

아주 만족스러운 성능을 뽑아낼 수 있는 좋은 제품이라 생각한다.

느려진 DBMS를 탓하기 전에 사용하고 있는 DBMS의 아키텍처는 얼마나 잘 이해하고 있으며,

설계와 관리는 어떻게 하고 있는가를 자문하는 시간을 먼저 가져보는 것이 좋지 않을까 생각한다.

'MSSQL' 카테고리의 다른 글

(펌) MSSQL Server 백업 / 복구 시나리오  (0) 2011.10.20
(펌) MSSQL 2005 백업계획작성  (0) 2011.10.20
(펌) MSSQL lock & blocking  (0) 2010.12.07
(펌) MSSQL Lock  (0) 2010.12.07
(펌) MSSQL SNAPSHOT 격리 수준  (0) 2010.12.07

+ Recent posts