bdfgdfg
[MSSQL] 트랜잭션(Transaction) 본문
트랜잭션
트랜잭션은 간단히 말하면 여러가지 액션(여기서는 SQL)을 원자성으로 처리한다는 의미이다.
게임을 예로들면 특히 거래소같은 시스템을 예로들 수 있다.
1. A계정이 거래소에 물건을 가격과 함께 등록. 거래소 테이블에는 해당 물건이 등록되었음.
2. B계정이 해당 물건을 구매. 거래소 테이블에 일치하는 행의 정보를 가져옴. (없다면 Fail)
3. B계정이 해당 물품을 올바르게 구매할 수 있다는 조건이 확인되면 해당 아이템을 구매. B 계정의 인벤토리와 관련된 테이블에 해당 물품 추가.
4. A계정에는 물품을 등록한 판매금을 지급. A계정의 인벤토리와 관련된 테이블에 재화를 추가하거나 업데이트.
5. 거래소 테이블에는 해당 물품의 행(Row)을 삭제
간단하게 설명하려고 해도 여러가지 액션이 일어난다.
근데 만약 B 계정의 2번까지 액션이 일어나다가, 다른 C계정이 B계정이 구매할려는 물품을 동일하게 구매할려고 할경우는 어떻게 될까? 정말 잘못동작된다면 B,C계정 모두가 A게정이 등록한 물품을 가져가버리는 문제가 발생할 수 있다.
즉. 위의 여러가지 액션은 두 가지 상황만 존재해야한다. 거래소의 물건을 올바르게 구매했거나, 실패했거나.(원자성)
-> 올바르게 동작(COMMIT 등록이라보면된다. ) 올바르지 않게 동작(ROLLBACK 철회라고 보면 된다.)
-> 기본적으로 트랜잭션을 명시하지않으면 자동으로 Commit.
트랜잭션의 특징
흔히 ACID라고 하며, 4가지의 특징이 존재한다.
1. 원자성(Atomicity) : 앞에서 말했듯 성공했거나/실패했거나 두 상황만 존재해야 한다. 중간이 없어야함.
-> 원자성이 보장되는 방법은 https://victorydntmd.tistory.com/m/129 여기에 정리가 잘 되어있음.
2. 일관성(Consistency) :
- 트랜잭션이 성공적으로 완료되면 일관적인 DB상태를 유지하는 것을 말합니다.
- 여기서 말하는 일관성이란, 위의 송금 예제에서 금액의 데이터 타입이 정수형(integer)인데, 갑자기 문자열(string)이 되지 않는 것을 말합니다.
- (좋은글이 딱히 없어 https://victorydntmd.tistory.com/m/129 여길 참고함)
3. 격리성(Isolation) : 둘 이상의 트랜잭션이 실행될 경우, 하나의 트랜잭션이 연산될 때 다른 트랜잭션이 끼어들 여지가 없음. 개념은 이렇지만 사실 Isolation은 격리 단계가 4가지로 나뉘어 트랜잭션의 실행중에도 Read를 통해 Commit되지 않은 상태의 결과를 Read할 수 있다.(Dirty Read라고 함)
4. 지속성(Durability) : 완료된 트랜잭션은 영구적으로 반영되어야한다.
BEGIN TRAN
INSERT INTO GameDB.dbo.Accounts VALUES(1,N'ID',N'PASSWORD');
COMMIT; -- 혹은 ROLLBACK
BEGIN TRAN구문이 이어지면 이 후 일어난 연산을 COMMIT할지 혹은 ROLLBACK할지를 정할 수 있다.
보통 트랜잭션을 위와같이만 쓰지 않고, Begin Try ~ End Try 및 Begin Catch ~ End Catch와 함께 사용한다.
즉 Begin Try영역에서 트랜잭션 영역이 오류없이 잘 수행되었다면 Commit. 그렇지않다면 Begin Catch로 간 다음 RollBack처리. (Begin Catch구문에서는 트랜잭션 실패 사유등을 따로 기록해두기도 한다)
CREATE TABLE GameItem
(
AccountUID INT NOT NULL,
ItemID INT NOT NULL,
ItemCount INT NOT NULL,
Registration DateTime2,
UpdateDate DateTIme2,
PRIMARY KEY(AccountUID, ItemID)
)
GO
BEGIN TRY
BEGIN TRAN
INSERT INTO GameDB.dbo.GameItem Values (44311,100,1,SYSDATETIME(),SYSDATETIME());
INSERT INTO GameDB.dbo.GameItem Values (44311,200,1,SYSDATETIME(),SYSDATETIME());
UPDATE GameDB.dbo.GameItem SET ItemCount = ItemCount + 1 WHERE AccountUID = 44311 AND ItemID = 100
COMMIT
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 -- 현재 활성화 된 트랜잭션 수를 반환.
ROLLBACK
END CATCH
이렇게 Item을 넣어주고, 업데이트까지 트랜잭션을 통해 원자적인 처리가 가능해진다.
+ 참고로 조건을 따져 Update와 Insert를 분리해야할 때 Merge문을 쓰면 편하다.
DECLARE @GetItemCount INT = 20;
DECLARE @ItemID INT = 100;
DECLARE @AccountUID INT = 44311
BEGIN TRY
BEGIN TRAN
MERGE INTO GameDB.dbo.GameItem AS TB1 -- 결과가 저장이 되는 테이블
USING (SELECT 1 AS DUAL) AS TB2 -- 기준이 되는 테이블. (단일 테이블 시 이처럼 더미를 통해 처리)
ON TB1.AccountUID = @AccountUID AND TB1.ItemID = @ItemID
WHEN MATCHED THEN -- 조건이 일치하는 경우 (UPDATE)
UPDATE SET ItemCount = ItemCount + @GetItemCount, UpdateDate = SYSDATETIME()
WHEN NOT MATCHED THEN -- 조건이 일치하지 않는 경우 (INSERT)
INSERT (AccountUID,ItemID,ItemCount,Registration,UpdateDate) VALUES (@AccountUID,@ItemID,@GetItemCount,SYSDATETIME(),SYSDATETIME());
COMMIT
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 -- 현재 활성화 된 트랜잭션 수를 반환.
ROLLBACK
END CATCH
-- update
기본적으로 트랜잭션안에는 꼭 원자적으로 실행될 연산들만 넣어야한다.
-> 성능 이슈가 존재함.
'CS > DB' 카테고리의 다른 글
[MSSQL] 복합 인덱스 (0) | 2022.10.17 |
---|---|
[MSSQL] 인덱스(Index) (0) | 2022.09.29 |