1. 들어가며: 왜 Lock을 배우기 전에 트랜잭션을 알아야 하는가?
데이터베이스에서 Lock(잠금)은 특정 자원에 대해 여러 요청이 동시에 들어올 때 데이터의 일관성을 지키기 위한 장치이다. 하지만 '언제까지 잠금을 유지할 것인가?', '어떤 단위로 잠금을 적용할 것인가?'라는 질문에 답하기 위해서는 반드시 트랜잭션(Transaction)의 개념이 선행되어야 한다.
트랜잭션은 데이터베이스의 상태를 변화시키는 하나의 논리적인 작업 단위를 의미한다. Lock은 바로 이 트랜잭션의 독립성을 보장하기 위해 사용되는 도구이다. 따라서 트랜잭션의 정의와 성질(ACID)을 명확히 이해해야만, 이후에 다룰 다양한 Lock의 종류와 격리 수준(Isolation Level)을 깊이 있게 파악할 수 있다.
2. 트랜잭션: 논리적 작업 단위의 개념 (All or Nothing)
2.1. 트랜잭션의 정의
데이터베이스에 삽입(Insert), 수정(Update), 삭제(Delete) 등의 작업을 수행할 때, 여러 개의 작업을 하나의 불가분한 단위로 묶어야 하는 경우가 발생한다. 이런 단위 자체를 트랜잭션이라 한다.
2.2. 시나리오를 통한 이해
온라인 쇼핑몰에서 상품을 구매하는 과정을 가정해 보자. 이 과정은 크게 두 단계의 데이터베이스 업데이트를 포함한다.
- 구매자의 잔고 차감: 구매자의 계좌에서 상품 금액만큼 차감한다.
- 판매자의 수익 증가: 판매자의 계좌에 상품 금액만큼 입금한다.
만약 1번 작업은 성공했으나 시스템 오류로 2번 작업이 실패한다면, 구매자의 돈은 사라지고 판매자는 돈을 받지 못하는 심각한 데이터 불일치가 발생한다. 트랜잭션은 이러한 상황을 방지하기 위해 "둘 다 성공하거나, 아니면 아예 수행되지 않은 상태(All or Nothing)"를 보장한다.
3. 현실 세계의 트랜잭션 시나리오 분석
현업에서는 수많은 트랜잭션이 동시에 발생한다. 예를 들어, 한정판 스니커즈 3,000개를 판매하는 이커머스 플랫폼에 수만 명의 고객이 동시에 주문을 시도하는 상황을 생각해 보자.
- 트랜잭션 A: 고객 1이 2,000개를 주문 (재고 확인 후 차감 시도)
- 트랜잭션 B: 고객 2가 2,000개를 주문 (재고 확인 후 차감 시도)
동시에 두 트랜잭션이 수행될 때, 각각 재고가 3,000개 남아있음을 확인하고 주문을 처리한다면 총 4,000개가 판매되어 재고가 -1,000개가 되는 오류가 발생할 수 있다. 이러한 문제를 방지하고 데이터의 일관성과 격리성을 유지하기 위해 값에 동시에 접근하지 못하도록 제어하는 기법을 동시성 제어(Concurrency Control)라 하며, 그 핵심 수단이 바로 Lock이다.
4.Transaction의 성질(ACID)
트랜잭션이 안전하게 수행됨을 보장하기 위해 갖추어야 할 네 가지 필수 성질을 ACID라 한다.
| 성질 | 설명 |
| 원자성 (Atomicity) | 트랜잭션 내의 모든 연산은 완전히 반영되거나, 전혀 반영되지 않아야 한다. (All or Nothing) |
| 일관성 (Consistency) | 트랜잭션 완료 후 데이터베이스는 미리 정의된 규칙(제약 조건 등)을 충족해야 한다. |
| 격리성 (Isolation) | 수행 중인 트랜잭션에 다른 트랜잭션이 끼어들어 연산 과정을 참조하거나 간섭할 수 없다. |
| 지속성 (Durability) | 성공적으로 완료된 트랜잭션의 결과는 시스템 장애가 발생하더라도 영구적으로 기록되어야 한다. |
5. 트랜잭션 제어의 기본: COMMIT과 ROLLBACK
트랜잭션을 종료하고 결과를 확정하거나 취소하기 위해 사용하는 핵심 명령어는 다음과 같다.
- COMMIT: 모든 작업이 정상적으로 처리되었음을 확정하는 명령어이다. COMMIT을 수행하면 변경된 내용이 데이터베이스에 영구적으로 반영되며, 다른 사용자들도 변경된 데이터를 볼 수 있게 된다.
- ROLLBACK: 작업 중 문제가 발생했을 때 현재 트랜잭션의 모든 변경 사항을 취소하고, 트랜잭션 시작 이전의 상태로 되돌리는 명령어이다.
6. 환경 구성
6.1. MySQL 컨테이너 띄우기
docker run -d \
-p 3399:3306 \
-e MYSQL_ROOT_PASSWORD=dingco \
mysql:8.0
6.2. 초기 DDL & DML 설정
CREATE DATABASE IF NOT EXISTS school;
USE school;
CREATE TABLE IF NOT EXISTS book (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
category VARCHAR(50) NOT NULL,
price INT NOT NULL,
is_adult BOOLEAN NOT NULL,
published_at DATE NOT NULL
);
-- 재귀 CTE를 이용한 1,000건 데이터 삽입
SET SESSION cte_max_recursion_depth = 1000;
INSERT INTO book (name, category, price, is_adult, published_at)
WITH RECURSIVE numbers AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM numbers WHERE n < 1000
)
SELECT
CONCAT('Book ', n),
CASE MOD(n, 5)
WHEN 0 THEN 'Fiction' WHEN 1 THEN 'Non-fiction' WHEN 2 THEN 'Science' WHEN 3 THEN 'History' ELSE 'Art' END,
FLOOR(9000 + (RAND() * 100000)),
CASE MOD(n, 2) WHEN 0 THEN TRUE ELSE FALSE END,
DATE_ADD('2000-01-01', INTERVAL FLOOR(RAND() * 9000) DAY)
FROM numbers;
7. 트랜잭션 실습
7.1. 주의 사항: Autocommit 설정
MySQL은 기본적으로 모든 쿼리를 실행 즉시 반영하는 Autocommit이 활성화되어 있다. 트랜잭션 실습을 위해서는 이를 수동 모드로 전환해야 한다.
SET AUTOCOMMIT=0;
7.2. ROLLBACK 테스트
데이터를 수정 및 삭제한 후 ROLLBACK을 통해 원복되는지 확인한다.
START TRANSACTION;
UPDATE book SET name = "트랜잭션 테스트" WHERE id = 1;
DELETE FROM book WHERE id = 2;
-- 현재 세션에서는 변경된 것으로 보임
SELECT * FROM book WHERE id IN (1, 2);
ROLLBACK;
-- 다시 조회 시 이전 상태로 복구됨을 확인
SELECT * FROM book WHERE id IN (1, 2);
7.3. COMMIT 테스트
특정 지점에서 COMMIT을 수행하여 데이터를 확정 반영한다.
START TRANSACTION;
UPDATE book SET name = "확정 데이터" WHERE id = 1;
COMMIT; -- 여기서 id=1의 변경 사항은 확정됨
DELETE FROM book WHERE id = 2;
ROLLBACK; -- COMMIT 이후의 DELETE 작업만 취소됨
-- id=1은 변경되어 있고, id=2는 그대로 남아있음
SELECT * FROM book WHERE id IN (1, 2);
이와 같이 트랜잭션을 활용하면 데이터의 무결성(Integrity)을 보장하고, 논리적으로 연관된 작업들을 안전하게 그룹화하여 처리할 수 있다.
'Database > MySQL' 카테고리의 다른 글
| [Lock-3][Optimization] Lock: 격리성 수준(Isolation Level) 심층 분석 및 실습 (0) | 2025.12.25 |
|---|---|
| [Lock-2][Optimization] 다중 트랜잭션 환경과 정합성 문제 (0) | 2025.12.24 |
| [Index-7][Optimization] 실무 적용(⭐) - 복합 인덱스/집계 테이블/반정규화 (0) | 2025.12.24 |
| [Index-6][Optimization] 실전 분석 (2) - 실행 계획 타입(Type) (0) | 2025.12.24 |
| [Index-5][Optimization] 실전 분석 (1) - 실행 계획의 이해 (0) | 2025.12.23 |
