요약
단일 UPDATE + WHERE로 처리 가능하면 SELECT FOR UPDATE가 필요 없다. 복잡한 로직이나 다중 테이블 일관성이 필요할 때만 사용한다.
본문
UPDATE + WHERE로 충분한 경우
UPDATE accounts
SET balance = balance - 30
WHERE id = 1 AND balance >= 30;
- atomic하게 처리됨
- affected rows로 성공 여부 판단
- 더 간단하고 효율적
SELECT FOR UPDATE가 필요한 경우
1. 읽은 값을 다른 곳에 써야 할 때
BEGIN;
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;
INSERT INTO history (prev_balance) VALUES (100);
UPDATE accounts SET balance = balance - 30 WHERE id = 1;
COMMIT;
2. 복잡한 비즈니스 로직이 애플리케이션에 있을 때
row = db.execute("SELECT * FROM orders WHERE id = 1 FOR UPDATE")
discount = calculate_discount(row, user_tier, coupon)
db.execute("UPDATE orders SET price = %s", row.price * discount)
3. 여러 테이블 간 일관성 유지
BEGIN;
SELECT * FROM orders WHERE id = 1 FOR UPDATE;
SELECT * FROM inventory WHERE product_id = 100 FOR UPDATE;
-- 둘 다 확인 후 처리
COMMIT;
판단 기준
| 상황 | 선택 |
|---|---|
| 단일 테이블 + SQL 조건 표현 가능 | UPDATE + WHERE |
| 복잡한 로직 / 다중 테이블 / 값 재사용 | SELECT FOR UPDATE |
참고
- SELECT FOR UPDATE는 행 수준 잠금을 획득한다
- InnoDB는 Next-Key Lock으로 팬텀 리드를 방지한다
- 트랜잭션 격리 수준
- 낙관적 락(version 컬럼)도 대안이 될 수 있음