F-LAB JAVA · 6주차 · Phase 4 · Connection Pool과 DB 세션
🏆 Phase 4 완주 — DB 동시성, 4주차와 만나는 지점
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
DB Lock 은 여러 세션이 같은 데이터를 동시에 수정해서 트랜잭션 원자성이 깨지는 것을 막기 위해 필요하며, 읽기용 공유 락 (S) 은 여러 세션이 동시에 가질 수 있지만 쓰기용 배타 락 (X) 은 한 세션만 가질 수 있는 점이 4주차 자바 synchronized 락과 닮았고, 두 트랜잭션이 서로의 락을 기다리면 데드락이 발생한다.
락이 필요한 이유는 — 세션1 이 데이터 수정 중 (commit 전) 인데 세션2 가 같은 데이터를 동시에 수정하면 세션1 이 rollback 했을 때 세션2 는 존재하지 않을 데이터 를 수정한 셈이 되어 트랜잭션 원자성이 깨지기 때문이다.
해결책은 트랜잭션 진행 중에는 다른 세션이 해당 데이터를 수정 못 하도록 락 을 거는 것이다.
락 종류는 (1) 공유 락 (S, Shared) — 읽기용, 여러 세션이 동시 보유 가능, (2) 배타 락 (X, eXclusive) — 쓰기용, 한 세션만 보유 가능이다.
이 구조는 4주차의 자바 synchronized 와 닮아 있고 — DB 도 두 트랜잭션이 서로의 락을 기다리는 데드락 이 발생할 수 있어 DB 가 감지·해결한다.
DB Lock = 도서관 책 접근:
락 필요 (원자성 보호):
- 책에 메모 중 (트랜잭션 A)
- 다른 사람이 동시에 메모 (트랜잭션 B)
- 충돌, 누구 메모인지 모름
공유 락 (S, 읽기):
- 여러 사람 동시 읽기 OK
- 책 보기만 (수정 X)
- S + S 호환
배타 락 (X, 쓰기):
- 메모 중엔 다른 사람 못 봄
- 한 사람만 점유
- X 시 다른 락 X
호환 표:
S X
S OK 대기
X 대기 대기
자바 synchronized:
- synchronized 도 한 스레드만
- 배타 락과 유사
- 단, DB 는 행/테이블 단위
데드락:
A: 책1 메모 중, 책2 대기
B: 책2 메모 중, 책1 대기
- 서로 기다림 (deadlock)
- DB 가 감지·하나 해제
→ DB Lock = 원자성 보호 (S/X), 4주차 synchronized 와 닮음, 데드락 가능.
1. 락이 필요한 이유
2. 트랜잭션 원자성 보호
3. 공유 락 (S)
4. 배타 락 (X)
5. 락 호환성
6. 4주차 synchronized와 닮음
7. 데드락
8. 락 모니터링/해제
9. Phase 4 완주 정리
동시 수정 문제:
세션1: 데이터 수정 중 (commit X)
세션2: 같은 데이터 동시 수정
→ 충돌
→ 데이터 무결성 깨짐
원자성 위험:
세션1 rollback 하면:
- 세션1 의 변경 사라짐
- 세션2 는 사라진 데이터 수정?
- → 잘못된 데이터 수정
→ 원자성 위반
해결 — 락:
트랜잭션 중:
- 다른 세션 수정 막음
- 락 보유
→ 격리 보장
동시성 vs 일관성:
락 없으면:
- 동시성 ↑
- 일관성 ↓ (위험)
락 있으면:
- 일관성 ↑
- 동시성 ↓ (대기)
→ 균형
-- 락 필요 시나리오 (ILIC)
-- 세션1 (배송 상태 변경 중)
BEGIN;
UPDATE shipments SET status = 'SHIPPED' WHERE id = 1;
-- commit 안 함
-- 세션2 (같은 배송 동시 변경 시도)
BEGIN;
UPDATE shipments SET status = 'CANCELLED' WHERE id = 1;
-- 락 없으면: 충돌, 어느 게 최종?
-- 락 있으면: 세션2 대기 (세션1 commit/rollback 까지)
-- 세션1 rollback 하면:
ROLLBACK;
-- 세션2: 락 풀림, CANCELLED 진행
-- → 락이 원자성 보호
DB Lock 이 필요한 이유는?
답:
1. 동시 수정:
원자성 위험:
해결:
균형:
원자성 (Atomicity):
트랜잭션 = 하나의 단위:
- 전부 성공 또는 전부 실패
- 중간 상태 X
→ Phase 6.2 에서 상세
락이 보호하는 것:
트랜잭션 진행 중:
- 변경된 데이터
- 아직 commit X
- 다른 세션 못 보거나 못 수정
→ 임시 상태 격리
격리 수준 (Phase 6.4 예고):
READ UNCOMMITTED:
- 락 약함
- Dirty Read 허용
READ COMMITTED:
- 커밋된 것만
REPEATABLE READ:
- 반복 일관
SERIALIZABLE:
- 가장 엄격
→ 락이 격리 수준 구현
락의 범위:
- 행 락 (Row Lock): 한 행
- 페이지 락: 페이지 단위
- 테이블 락: 테이블 전체
- 갭 락: 범위 (InnoDB)
→ 작을수록 동시성 ↑
-- 원자성 보호 (ILIC)
-- 트랜잭션 A: 배송 상태 + 운임 갱신
BEGIN;
UPDATE shipments SET status = 'SHIPPED' WHERE id = 1;
-- ↑ shipments.id=1 에 배타 락
UPDATE freights SET amount = 1000 WHERE shipment_id = 1;
-- ↑ freights 의 해당 행에 배타 락
-- 동시 다른 트랜잭션:
-- 같은 행 수정 시도 → 대기
-- 같은 행 조회 (격리 수준 따라):
-- - REPEATABLE READ: 이전 값
-- - SERIALIZABLE: 대기
COMMIT;
-- 락 해제, 다른 트랜잭션 진행
-- → 트랜잭션 A 동안:
-- "두 UPDATE 가 한 단위" 보장 (원자성)
트랜잭션 원자성 보호와 락의 관계는?
답:
1. 원자성:
락 보호:
격리 수준:
범위:
공유 락 (S, Shared Lock):
읽기용 락:
- 여러 세션 동시 보유
- 읽기 일관성 보장
- 다른 세션 수정 막음
공유 락 동작:
세션1: SELECT (S 락)
세션2: SELECT (S 락) — OK (호환)
세션3: UPDATE (X 락) — 대기
→ 읽기는 동시, 쓰기는 막음
-- 명시적 공유 락 (MySQL InnoDB)
BEGIN;
SELECT * FROM shipments WHERE id = 1 LOCK IN SHARE MODE;
-- 또는 (SQL 표준)
SELECT * FROM shipments WHERE id = 1 FOR SHARE;
-- 공유 락 획득 (읽기, 다른 읽기 OK, 쓰기는 대기)
자동 획득:
격리 수준 따라:
- REPEATABLE READ: 일부 자동
- SERIALIZABLE: SELECT 도 S 락
→ DB 가 자동
-- 공유 락 (ILIC)
-- 운임 계산 (배송 정보 일관 읽기)
BEGIN;
SELECT * FROM shipments WHERE id = 1 FOR SHARE; -- S 락
-- 다른 세션도 SELECT FOR SHARE OK (같이 읽기)
-- 다른 세션 UPDATE 시도 → 대기
-- 운임 계산
SELECT weight, distance FROM shipment_details WHERE shipment_id = 1;
-- 운임 저장
-- ...
COMMIT;
-- S 락 해제
-- → 계산 중 다른 세션이 수정 못 함 (일관성)
-- → 다른 세션이 같이 읽기는 가능 (성능)
공유 락 (S) 의 동작은?
답:
1. 공유 락:
동작:
사용:
자동:
배타 락 (X, eXclusive Lock):
쓰기용 락:
- 한 세션만 보유
- 다른 모든 락 차단
- 독점
배타 락 동작:
세션1: UPDATE (X 락)
세션2: SELECT — 대기 (격리 수준 따라)
세션3: UPDATE — 대기
→ 한 세션만 독점
자동 획득:
쓰기 SQL 시:
- UPDATE → X 락
- DELETE → X 락
- INSERT → X 락 (행)
→ 자동
-- 명시적 배타 락
BEGIN;
SELECT * FROM shipments WHERE id = 1 FOR UPDATE;
-- X 락 (다른 세션 SELECT FOR UPDATE / UPDATE 대기)
-- 데이터 확인 후 수정
UPDATE shipments SET status = 'SHIPPED' WHERE id = 1;
COMMIT;
-- 배타 락 (ILIC)
-- 시나리오: 재고 차감 (동시성 위험)
BEGIN;
SELECT stock FROM warehouse_items WHERE id = 1 FOR UPDATE;
-- X 락 (다른 세션 같은 행 수정 대기)
-- 재고 확인
-- stock = 10
-- 차감
UPDATE warehouse_items SET stock = stock - 5 WHERE id = 1;
-- stock = 5
COMMIT;
-- X 락 해제
-- 다른 세션이 동시에 같은 작업 시:
-- → 대기 → 첫 세션 commit 후 진행
-- → 재고 중복 차감 방지 (원자성)
배타 락 (X) 의 동작은?
답:
1. 배타 락:
동작:
자동:
명시:
| S (다른 세션) | X (다른 세션) | |
|---|---|---|
| 현재 S | OK (호환) | 대기 |
| 현재 X | 대기 | 대기 |
S + S = OK:
두 세션 모두 읽기:
- 충돌 X
- 동시 가능
- 일관성 유지
→ 읽기 동시성
S + X 또는 X + S = 대기:
읽기 중 쓰기:
- 데이터 변경 위험
- 대기
쓰기 중 읽기:
- 미완 데이터 위험
- 대기
→ 격리
X + X = 대기:
두 세션 쓰기:
- 충돌 위험 (최대)
- 대기
→ 순차 처리
락 호환성 (ILIC)
시나리오:
세션1: UPDATE shipments WHERE id=1 (X 락)
세션2 시도:
- SELECT WHERE id=1 — 격리 수준 따라 (대부분 OK, 이전 값)
- SELECT FOR SHARE — 대기 (S vs X)
- SELECT FOR UPDATE — 대기 (X vs X)
- UPDATE WHERE id=1 — 대기 (X vs X)
세션1: COMMIT
- 락 해제
- 세션2 진행
세션1: UPDATE id=2
- 다른 행 (X 락 별개)
- 세션2 의 id=1 작업과 무관
→ 행 단위 락 (행 다르면 동시 가능)
S/X 호환성 표는?
답:
1. S+S:
S+X / X+S:
X+X:
원리:
DB 락 ↔ synchronized:
공통:
- 동시 접근 제어
- 한 번에 한 주체 (배타)
- 다른 주체 대기
- 충돌 방지
| 항목 | synchronized | DB Lock |
|---|---|---|
| 대상 | 자바 객체 | DB 행/페이지/테이블 |
| 주체 | 스레드 | 세션 (트랜잭션) |
| 종류 | 배타만 | S/X |
| 해제 | 블록 종료 | commit/rollback |
4주차 가시성/원자성:
자바: 멀티스레드 + 공유 변수
- synchronized
- volatile
- Atomic
DB: 멀티세션 + 공유 데이터
- Lock
- 격리 수준
- 트랜잭션
→ 같은 문제, 다른 계층
5주차 싱글톤 빈 동시성:
싱글톤 빈 + 멀티스레드:
- 4주차 도구 (Atomic, volatile)
DB + 멀티세션:
- DB 락
- 트랜잭션
→ 모든 계층에 동시성 문제
// 닮음 (ILIC)
// 자바 락 (싱글톤 빈, 4주차)
@Service
public class ShipmentCounter {
private final AtomicLong count = new AtomicLong(); // 4주차 CAS
public void increment() {
count.incrementAndGet(); // 자바 락 (스레드 안전)
}
}
// DB 락 (트랜잭션, 6주차)
@Service
public class ShipmentService {
@Transactional
public void decrementStock(Long itemId, int amount) {
// SELECT FOR UPDATE (배타 락)
// UPDATE stock = stock - ?
// → DB 락 (세션 간 안전)
}
}
// → 동시성 = 모든 계층 (스레드, 세션)
// → 닮은 원리, 다른 계층
4주차 자바 synchronized 와 닮음은?
답:
1. 공통:
차이:
4주차 연결:
모든 계층:
데드락 (Deadlock):
두 트랜잭션이 서로의 락 대기:
- 무한 대기
- 진행 불가
→ DB 가 감지·해제
데드락 발생:
트랜잭션 A:
- shipments.id=1 락 (점유)
- bookings.id=10 락 대기
트랜잭션 B:
- bookings.id=10 락 (점유)
- shipments.id=1 락 대기
→ 서로 대기 (무한)
DB 의 감지:
주기적 검사:
- 대기 그래프 (Wait-for graph)
- 순환 감지
감지 시:
- 한 트랜잭션 강제 rollback
- 다른 트랜잭션 진행
→ 자동 해결
데드락 예방:
- 일정한 순서로 락 획득
(예: 항상 id 작은 것부터)
- 짧은 트랜잭션
- 적절한 격리 수준
- SELECT FOR UPDATE 신중
// 데드락 위험 (ILIC)
// ❌ 위험: 순서 다름
@Service
public class TransferService {
@Transactional
public void transfer1(Long from, Long to) {
// from 먼저 락, to 나중
accountDao.lock(from); // SELECT FOR UPDATE
accountDao.lock(to);
}
@Transactional
public void transfer2(Long from, Long to) {
// to 먼저 락, from 나중 (순서 반대)
accountDao.lock(to);
accountDao.lock(from);
}
// transfer1(A→B) + transfer2(B→A) 동시 → 데드락
}
// ✓ 안전: 일정한 순서
@Service
public class SafeTransferService {
@Transactional
public void transfer(Long from, Long to) {
Long first = Math.min(from, to); // 항상 작은 ID 먼저
Long second = Math.max(from, to);
accountDao.lock(first);
accountDao.lock(second);
// → 데드락 X (순서 일정)
}
}
class AccountDao { void lock(Long id) {} }
데드락은?
답:
1. 데드락:
발생:
DB 감지:
예방:
-- 락 확인 (MySQL InnoDB)
-- 현재 락 (8.0+)
SELECT * FROM performance_schema.data_locks;
-- 대기 중인 락
SELECT * FROM performance_schema.data_lock_waits;
-- 트랜잭션
SELECT * FROM information_schema.innodb_trx;
락 해제:
자동:
- commit
- rollback
- 세션 종료
강제:
- KILL 세션 ID
- 운영자
락 타임아웃:
innodb_lock_wait_timeout (MySQL):
- 기본 50초
- 초과 시 자동 rollback
- "Lock wait timeout exceeded"
→ 무한 대기 방지
운영 팁:
- 긴 트랜잭션 모니터링
- 락 대기 알람
- 데드락 로그 확인
- SELECT FOR UPDATE 신중
- 격리 수준 조정
-- 락 모니터링 (ILIC MySQL)
-- 현재 락 상태
SELECT
OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE, LOCK_STATUS
FROM performance_schema.data_locks
WHERE OBJECT_NAME LIKE 'shipments';
-- 락 대기
SELECT
r.trx_id AS waiting_trx,
r.trx_query AS waiting_query,
b.trx_id AS blocking_trx,
b.trx_query AS blocking_query
FROM information_schema.innodb_lock_waits w
JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id
JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id;
-- 문제 트랜잭션 강제 종료
KILL <trx_id>;
-- 설정 확인
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';
DB 락 모니터링/해제는?
답:
1. 모니터링:
해제:
타임아웃:
운영:
Phase 4 — Connection Pool과 DB 세션
Unit 4.1 — 매 요청 연결 비효율
- TCP/인증/세션 비용
- 본말전도
Unit 4.2 — Connection Pool ★깊이
- 미리 N 개, 재사용
- HikariCP
Unit 4.3 — DB 세션과 연결
- Connection ↔ Session 1:1
- 풀 N = 세션 N
Unit 4.4 — DB Lock
- S/X 락
- 4주차 연결, 데드락
Phase 4 핵심 메시지:
"DB 연결은 비싸므로 Connection Pool 로
N 개 미리 만들고 재사용한다.
풀의 Connection 은 DB 세션과 1:1 매핑되며,
세션 간 충돌은 락으로 막는다."
Phase 4 → Phase 5:
- Connection 비용/구조 → 추상화
Phase 5 — DataSource (5.3 ★깊이):
- 다양한 커넥션 획득 방식
- 추상화 필요성
- DataSource 인터페이스 ★깊이
- DriverManagerDataSource
| Q | 핵심 답변 |
|---|---|
| 락 이유? | 원자성 보호 |
| 공유 락? | 읽기, 여러 세션 |
| 배타 락? | 쓰기, 한 세션 |
| 호환성? | S+S OK, X 단독 |
| synchronized 닮음? | 동시 접근 제어 |
| 데드락? | 서로 락 대기 |
| 예방? | 일정한 순서 |
| 타임아웃? | innodb_lock_wait_timeout |
| 모니터링? | performance_schema |
| 행 락? | InnoDB |
답:
답:
답:
답:
답:
1. DB Lock 의 필요성
2. 4주차 synchronized 와 닮음
3. 데드락
🔗 Phase 4 — Connection Pool과 DB 세션
✅ Unit 4.1 매 요청마다 연결의 비효율
✅ Unit 4.2 Connection Pool ★깊이
✅ Unit 4.3 DB 세션과 연결 구조
✅ Unit 4.4 DB Lock 개념 ← 여기, Phase 4 완주
→ 연결 비용 (TCP/인증/세션)
→ 풀 (재사용, HikariCP)
→ Connection ↔ Session 1:1
→ 락 (원자성 보호, 4주차 연결)
💾 Phase 5 — DataSource 인터페이스
Unit 5.1 — 다양한 커넥션 획득 방식
Unit 5.2 — 추상화 필요성
Unit 5.3 — DataSource 인터페이스 ★깊이
Unit 5.4 — DriverManagerDataSource
🧪 Part A (9 Unit) ✅
💾 Part B — DB 접근의 진화
✅ Phase 3 — JDBC (3)
✅ Phase 4 — Connection Pool (4) ← 완주
⏭ Phase 5 — DataSource (4, 5.3 ★깊이)
총: 16/28 Unit
🏆 Phase 4 완주 — Connection Pool과 DB 세션