백엔드 개발자가 고려해야 하는 트랜잭션/dbLock(추가개념 : DDL, DML, DCL, TCL)
1. 트랜잭션과 Isolation (격리 수준)
1) 트랜잭션 (Transaction)
트랜잭션은 데이터베이스의 상태를 변화시키는 하나의 논리적 작업 단위입니다. 데이터의 무결성을 보장하기 위해 "전부 성공하거나, 전부 실패해야 한다(All or Nothing)"는 원칙을 가집니다.
[ 송금 요청 시작 ]
│
▼
[ START TRANSACTION ]
│
▼
[단계 1] 홍길동 계좌 5만원 차감 (DML)
│
▼
[단계 2] 이몽룡 계좌 5만원 가산 (DML)
│
▼
{ 두 작업 모두 정상 처리되었는가? }
│
┌────────────────┴────────────────┐
[ Yes ] [ No ]
│ │
▼ ▼
[ COMMIT ] [ ROLLBACK ]
(DB에 물리적 영구 반영) (단계 1을 포함하여 전면 취소)
│ │
▼ ▼
[종료: 홍길동-5만 / 이몽룡+5만] [종료: 두 계좌 모두 처음 상태로 복구]2) Isolation (격리성)
여러 트랜잭션이 동시에 실행될 때, 서로의 작업에 물리적으로 간섭하지 못하도록 독립성을 보장하는 성질입니다. 격리성이 높을수록 데이터는 안전하지만, 동시 처리 성능은 떨어지는 트레이드오프 관계를 가집니다.
2-1) Isolation의 종류와 발생 가능한 현상
안전성이 낮은 순서부터 높은 순서로 총 4가지 단계가 존재합니다.
- Read Uncommitted (레벨 0): 다른 트랜잭션이 커밋하지 않은 미확정 데이터도 읽을 수 있습니다.
- Read Committed (레벨 1): 커밋이 완료된 데이터만 읽을 수 있습니다. 대부분의 RDBMS(Oracle, PostgreSQL 등)에서 기본값으로 사용합니다.
- Repeatable Read (레벨 2): 한 트랜잭션 내에서 같은 조회를 반복해도 항상 동일한 결과를 보장합니다. MySQL InnoDB의 기본값입니다.
- Serializable (레벨 3): 가장 엄격한 격리 수준으로, 트랜잭션이 순차적으로 실행되는 것처럼 완벽한 격리를 제공합니다.
• Dirty Read A 트랜잭션이 수정한 데이터를 커밋하기 전에 B 트랜잭션이 읽는 현상 (A가 롤백되면 B는 가짜 데이터를 본 셈이 됨). • Non-Repeatable Read 한 트랜잭션 내에서 같은 조회를 두 번 했는데, 중간에 다른 트랜잭션이 값을 수정/삭제하여 두 결과가 다르게 나오는 현상. • Phantom Read 한 트랜잭션 내에서 범위 조회를 두 번 했는데, 중간에 다른 트랜잭션이 새로운 데이터를 삽입(Insert)하여 두 번째 조회 때 유령(Phantom)처럼 새로운 행이 나타나는 현상.
*비지니스 로직에 따른 예외허용
1. 격리 수준 허용 전략 비교
2. 시나리오별 메커니즘 및 자원 흐름
[시나리오 1] 실시간 매출 대시보드 (Dirty Read 허용)
- 상황
초당 수만 건의 주문 결제(
UPDATE/INSERT)가 휘몰아치는 상황 - 데이터 흐름
[결제 엔진] ──(미커밋 상태 데이터 발생)──> [DB 메모리]
│ (락 없이 즉시 읽기)
[경영진 대시보드] <────────────────────────────┘- 결론 1원 단위의 오차보다 지연 없는 실시간 매출 추이 시각화가 훨씬 중요하므로, 락을 완벽히 제거하여 쓰기 엔진의 속도를 극대화합니다.
[시나리오 2] SNS 타임라인 피드 스크롤 (Non-Repeatable Read 허용)
- 상황 사용자가 인스타그램 피드를 아래로 길게 스크롤하며 읽는 도중, 다른 유저가 중간에 위치한 게시글을 수정하거나 삭제한 상황
- 데이터 흐름
[유저 스크롤 시작] ──> 1페이지 조회 (A글 내용: "안녕하세요")
[타인 행동] ─────────> A글 내용 수정 및 커밋 ("반갑습니다")
[유저 스크롤 지속] ──> 2페이지 조회 시, 수정된 A글이 자연스럽게 반영되어 노출됨- 결론 트랜잭션이 시작된 시점의 과거 데이터를 고집하는 것보다, 사용자가 진행 중인 화면에서 자연스럽게 최신 변경 사항을 보게 만드는 것이 훨씬 더 좋은 사용자 경험(UX)을 제공합니다.
[시나리오 3] 대용량 이벤트 로그 수집 (Phantom Read 허용)
- 상황
초당 수십만 건의 서버 방화벽 로그(
INSERT)가 유입되는 시스템에서 보안관제관이 최근 1시간 이력을 분석하는 상황 - 데이터 흐름
[보안관제 분석] ──> 최근 1시간 로그 범위 조회 (기존 데이터 Lock으로 보호)
[로그 수집 엔진] ─> 새로운 로그 INSERT 진입 시도 (조건 범위 탐색 무시하고 즉시 삽입)- 결론 관제관이 읽은 기존 이력의 수정/삭제만 방어하면 충분하므로, 굳이 범위 잠금(Range Lock)을 걸어 전사 로그 수집 패킷이 줄 서서 대기하다가 큐가 터지는 가용성 장애를 원천 차단합니다.
2-2) Isolation에 따른 DB Lock의 매커니즘 설명
격리 수준을 달성하기 위해 데이터베이스는 공유락(Shared Lock, S-Lock)과 배타락(Exclusive Lock, X-Lock)을 내부적으로 제어합니다.
- Read Uncommitted 데이터를 읽을 때 공유락을 아예 걸지 않습니다. 쓰기 작업 시에만 배타락을 걸기 때문에 다른 트랜잭션이 커밋 안 된 데이터를 그냥 읽을 수 있습니다.
- Read Committed 데이터를 읽는 순간에만 공유락을 걸고, 조회 쿼리가 끝나자마자 즉시 락을 해제합니다. 따라서 한 트랜잭션 안에서 잠시 후 다시 조회하면 다른 트랜잭션이 수정한 값이 보일 수 있습니다.
- Repeatable Read 데이터를 조회할 때 생성된 공유락을 조회가 끝나도 바로 풀지 않고, 트랜잭션이 최종 커밋되거나 롤백될 때까지 유지합니다. 이로 인해 트랜잭션 도중 다른 사용자가 데이터를 수정할 수 없습니다.
- Serializable 조회하는 데이터뿐만 아니라, 조회 범위에 해당하는 영역 전체에 레인지 락(Range Lock)이나 갭 락(Gap Lock)을 걸어 다른 트랜잭션이 새로운 레코드를 진입시키는 것 자체를 원천 봉쇄합니다.
2. DB Lock의 단계(Granularity)와 성능 관계
1) 데이터베이스 락의 범위 단계
잠금의 대상을 어느 규모로 잡느냐에 따라 자원의 격리 범위가 달라집니다.
- 테이블 락 (Table Lock): 테이블 전체를 잠그는 방식입니다. 데이터 구조를 변경하는 DDL 쿼리나 테이블 전체의 데이터를 대형 수정할 때 발생하며, 다른 트랜잭션은 해당 테이블에 접근할 수 없습니다.
- 파티션 락 (Partition Lock): 테이블이 파티셔닝되어 있을 때, 특정 파티션 구역(디스크 파일 구역)만 잠그는 방식입니다. 다른 파티션 영역은 정상적으로 동시 조회가 가능하므로 테이블 락보다 효율적입니다.
- 레인지 락 (Range Lock) / 갭 락 (Gap Lock): 특정 조건 범위(
WHERE id BETWEEN 10 AND 20)에 락을 걸거나, 데이터와 데이터 사이의 빈 공간(Gap)을 잠가서 새로운 데이터가 끼어들지 못하게 막는 정밀한 잠금 방식입니다. - 로우 락 (Row Lock / Record Lock): 단 하나의 행(Row) 레코드만 잠그는 방식입니다. 실무 비즈니스 로직에서 가장 빈번하게 발생하며, 동시성을 해치지 않는 가장 이상적인 잠금 범위입니다.
2) DB Lock과 성능 간의 관계
락의 범위(Granularity)는 시스템의 동시성 성능과 데이터 무결성 간의 시소게임입니다.
[락의 범위가 커질수록: Table ──> Partition ──> Range ──> Row]
- 데이터 무결성(안전성): 증가 ───> 데이터가 완전히 보호됨
- 동시 처리 성능(Concurrency): 감소 ───> 다른 트랜잭션들이 줄 서서 대기해야 함
[락의 범위가 작아질수록: Row ──> Range ──> Partition ──> Table]
- 동시 처리 성능(Concurrency): 증가 ───> 여러 사용자가 동시에 각자 다른 행을 수정 가능
- 관리 오버헤드: 증가 ───> 수많은 락 객체를 메모리에서 제어해야 하므로 DB 부하 증가 및 데드락(Deadlock) 위험 노출3. 추가 개념: SQL의 분류 (DCL, DML, DDL)
데이터베이스 명세에서 명령어의 목적에 따라 관리, 조작, 정의 언어로 구분합니다.
1) DCL (Data Control Language - 데이터 제어어)
데이터베이스의 무결성, 보안, 권한 제어, 회복 등을 관리하기 위해 사용하는 내부 통제용 명령어입니다. 주로 DBA(데이터베이스 관리자)가 사용합니다.
- GRANT: 특정 사용자에게 테이블이나 객체에 대한 권한을 부여합니다.SQL
GRANT SELECT, INSERT ON users TO hyeonjin_dev;- REVOKE: 부여했던 사용자의 권한을 다시 회수합니다.SQL
REVOKE INSERT ON users FROM hyeonjin_dev;2) DML (Data Manipulation Language - 데이터 조작어)
개발자가 가장 많이 사용하는 영역으로, 테이블 내의 실질적인 데이터(행)를 조회, 삽입, 수정, 삭제하기 위해 사용하는 명령어입니다. 트랜잭션의 제어를 받습니다.
- SELECT: 데이터를 조회합니다.SQL
SELECT * FROM users WHERE status = 'ACTIVE';- INSERT: 새로운 데이터를 삽입합니다.SQL
INSERT INTO users (name, email) VALUES ('김현진', 'dev@example.com');- UPDATE: 기존 데이터를 수정합니다.SQL
UPDATE users SET status = 'BLACK' WHERE id = 45;- DELETE: 데이터를 삭제합니다.SQL
DELETE FROM users WHERE id = 45;3) DDL (Data Definition Language - 데이터 정의어)
데이터베이스 객체(테이블, 인덱스, 뷰, 파티션 등)의 구조 자체를 생성, 변경, 삭제하는 명령어입니다. 실행 즉시 DB에 반영(Auto Commit)되는 특성을 가집니다.
- CREATE: 데이터베이스 객체를 신규 생성합니다.SQL
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50)
);- ALTER: 기존 객체의 구조를 변경(컬럼 추가, 타입 변경 등)합니다.SQL
ALTER TABLE users ADD COLUMN update_date TIMESTAMP;- DROP: 객체 자체를 완전히 삭제합니다. (복구 불가)SQL
DROP TABLE users;- TRUNCATE: 테이블의 구조만 남기고 데이터 전체를 완전히 최초 상태로 초기화(디스크 공간 회수)합니다. DELETE와 달리 롤백이 불가능한 DDL 명령어입니다.SQL
TRUNCATE TABLE users;4) TCL (Transaction Control Language - 트랜잭션 제어어)
- COMMIT: 현재 트랜잭션 범위 내에서 수행된 모든 DML 변경 사항을 데이터베이스 파일에 물리적으로 영구 반영합니다.
COMMIT;- ROLLBACK: 현재 트랜잭션의 모든 변경 사항을 전면 취소하고, 마지막 COMMIT 직후 또는 지정된 특정 SAVEPOINT 시점으로 데이터를 되돌립니다.
ROLLBACK;- SAVEPOINT: 트랜잭션 진행 흐름 중간에 에러 발생 시 부분 복구(롤백)를 수행할 수 있도록 임시 저장 책갈피를 지정합니다.
SAVEPOINT sp_checkpoint;
댓글
GitHub 계정으로 의견이나 질문을 남길 수 있습니다.