마스터(acct) - 상세(acct_txn) 구조와 InnoDB 엔진의 실제 락 규칙
1. 계좌 마스터 (acct): 계좌의 최종 상태 관리
acct_no (PK): 계좌번호 ('10288000000001')balance_amt: 최종 잔액 (초기값 1,000,000원)CHECK (balance_amt >= 0): 음수 잔액 차단 제약2. 거래 원장 (acct_txn): 개별 거래 이력
txn_id (PK · 대리키), txn_unique_no (UNIQUE · 거래고유번호)txn_amt (거래금액)acct_no (FK): acct(acct_no)를 참조하는 외래키InnoDB는 행 단위로 두 종류의 락을 겁니다.
acct_txn)에 INSERT할 때, 부모(acct) 계좌가 사라지지 않도록 엔진이 부모 행에 자동으로 S락을 걺!FK 검증 시 암묵적으로 부여되는 S락과 잔액 UPDATE X락의 교차 대기
동일 계좌(100만 원)에 세션 A와 B가 동시에 1만 원씩 입금할 때, 두 거래 모두 승인되어 최종 잔액 1,020,000원이 되어야 함.
INSERT INTO acct_txn ... VALUES (...); -- ① 원장 기록 UPDATE acct SET balance_amt = balance_amt + 10000; -- ② 잔액 갱신
A와 B 터미널의 교차 실행 순서와 OBS 관측 뷰(v_locks, v_waits) 모니터링
시작부터 배타락(X)을 획득하여 S↔X 교차 대기 조건을 원천 차단
두 입금이 동시 요청되어도 데드락 에러 없이, 큐(Queue)처럼 차례대로 실행되어 두 건 모두 100% 누적 반영되어야 함.
BEGIN; -- ★ 핵심: 조회하면서 바로 쓰기락(X) 획득 SELECT balance_amt INTO @bal FROM acct WHERE acct_no = '...' FOR UPDATE; INSERT INTO acct_txn ...; UPDATE acct SET balance_amt = ...; COMMIT;
FOR UPDATE로 X락을 먼저 쥠. B는 S락조차 못 쥐고 입구에서 대기하므로 "둘 다 S락을 쥔 상태" 자체가 만들어지지 않음!
A의 선제적 X락 점유로 B가 시작 지점에서 대기하고, A 커밋 후 B가 정상 완료되는 흐름
락 없는 단순 조회 판정의 위험성과 CHECK 제약조건이 사고를 못 막는 이유
잔액 100만 원 계좌에서 동시 70만 원 출금 시, 반드시 1명만 승인되고 나머지 1명은 잔액 부족으로 거절되어야 함.
-- [PART A: 앱 변수 덮어쓰기] UPDATE acct SET balance_amt = @bal - 700000; -- [PART B: DB 직접 차감] UPDATE acct SET balance_amt = balance_amt - 700000;
"CHECK는 잔액은 지키지만, 잘못된 판단은 막지 못한다"
B는 100만 원을 보고 이미 승인 판단을 내린 뒤에 실패했습니다. 판단 직후 ATM이 현금을 내줬다면 돈은 나갔는데 전산 기록은 없는 사고가 됩니다. CHECK는 마지막 방어선이지 올바른 처리 방법이 아닙니다.
PART A: 에러 없이 70만 원이 증발하는 과정 vs PART B: 사후 CHECK 에러 폭탄
화면 조회는 잠그지 않는 것이 맞고, 출금·이체 판단에 쓰는 조회만 잠근다
① 고객의 잔액 조회 화면은 다른 채널에서 출금이 진행 중이어도 막히지 않아야 함.
② 출금·이체 판단은 다른 거래가 끼어들지 못하는 최신 확정 잔액으로 해야 함.
-- [세션 1] 이체 판단용 조회 (잠그지 않음) SELECT balance_amt INTO @bal FROM acct ...; -- 100만 -- [세션 2] ATM 전액 출금 후 COMMIT UPDATE acct SET balance_amt = balance_amt - 1000000; COMMIT; -- [세션 1] @bal(100만)을 믿고 이체 진행
FOR UPDATE: 읽는 순간 잠가서, 판단부터 처리까지 아무도 끼어들지 못하게 함.A가 X락을 쥐고 출금 중이어도 B의 일반 조회는 기다리지 않고, 락을 요청하는 조회만 기다리는 흐름
시나리오 4: SELECT FOR UPDATE를 통한 "조건 검증-잔액 차감-원장 기입" 동기화
동시 70만 원 출금 시, B가 100만 원이라는 낡은 잔액을 보고 헛된 승인 판단을 내리지 않도록 판정 단계부터 대기시켜야 함.
BEGIN; -- ① 보는 순간 X락으로 계좌 잠금 SELECT balance_amt INTO @bal FROM acct ... FOR UPDATE; -- ② 판단 SELECT IF(@bal >= 700000, '승인', '거절 - 잔액 부족'); -- ③-a 승인이면 UPDATE acct SET balance_amt = balance_amt - 700000 ...; INSERT INTO acct_txn (…) VALUES (…); COMMIT; -- ③-b 거절이면 ROLLBACK;
if 문으로 구현합니다.SELECT ... FOR UPDATE에서 대기.B가 잔액 조회 자체를 대기하고, A의 70만 원 출금 완료 후 30만 원을 보고 스스로 거절하는 흐름
같은 기능끼리의 경합과, 서로 다른 기능 사이의 간섭을 함께 다뤄야 한다
"같은 기능이 동시에 실행될 때만 신경 쓰면 된다?"
"한 거래 안의 단계 순서, 그리고 판단과 실행 사이의 틈"