207. 유령 읽기 (Phantom Read) - 병행 수행 문제점 동시성 제어 격리성 위배 INSERT DELETE 행(Row) 개수 불일치 범위 쿼리(Range Query) 에러
핵심 인사이트: (202번 5번 문제 심화) 내 방에 사람이 몇 명 있는지 세어보았다. "1명, 2명, 3명... 총 3명이네!" (1차 조회 끝). 10초 뒤 다시 세어보았다. "1명, 2명, 3명, 4명... 엥? 총 4명?!" 분명 방 문을 닫아두고 나 혼자만 세고 있었는데 언제 1명이 더 늘어난 거지?! "야! 네가 방 안의 사람들 옷 색깔(기존 데이터)을 못 바꾸게 자물쇠(Shared Lock)를 꽉 채워놨지? 근데 밖에서 들어오는 현관문을 안 잠갔잖아!! 네가 고개 돌린 사이에 새로운 사람 1명이 현관문을 열고 스르륵 방으로 들어와 버렸네(INSERT)! 그래서 방 안의 '사람 명수(행 개수)'가 늘어나서 두 번째 셀 때 귀신이 나타난 것처럼 보인 거잖아!!" 기존 데이터가 아닌, 없던 데이터가 허공에서 튀어나와 멘탈을 터뜨리는 폴터가이스트 현상, 유령 읽기다.
Ⅰ. 모순성(Unrepeatable Read)과의 결정적 차이 🌟 헷갈림 주의 🌟
둘 다 똑같은 걸 두 번 읽었을 때 앞뒤가 다릅니다. 하지만 범인이 다릅니다.
- 모순성(204번): 원래 있던 A라는 사람의 옷 색깔(Data 값)이 파란색에서 빨간색으로 바뀐 것 (UPDATE 공격).
- 유령 읽기(Phantom Read): 원래 있던 사람은 그대로인데, 갑자기 구석에 없던 B라는 귀신(새로운 Data 행 Row)이 툭 하고 추가로 생겨난 것 (INSERT 공격).
Ⅱ. 유령 읽기 (Phantom Read)의 탄생 시나리오 🌟 핵심 기출 🌟
데이터 1개를 콕 집는 게 아니라, WHERE age >= 20 같은 **'범위 쿼리(Range Query)'**를 날릴 때 무조건 터지는 재앙입니다.
- 은행 본사 감사팀(
T1)이 통계 보고서를 쓰려고 쿼리를 날립니다. "현재 잔고 1천만 원 이상인 VIP 고객 명단 다 뽑아와!" - DB가 1번부터 3번 고객까지 총 3명의 명단을 보여줍니다 (1차 쿼리 완료).
- 찰나의 순간, 영업팀 직원(
T2)이 1천만 원짜리 신규 VIP 고객을 한 명 새로 가입시켜 DB에 쓱 추가해 버리고 성공(INSERT & COMMIT) 합니다. - 감사팀(
T1)이 보고서 검증을 위해 1분 뒤에 아까랑 똑같은 "잔고 1천만 원 이상 VIP 명단 가져와!" 쿼리를 다시 날립니다. - 엥?! 아까 분명 3명이었는데, 방금 전엔 없었던 '4번 고객'이라는 유령 1명이 명단에 스르륵 추가되어 총 4명이 검색됩니다.
- 대참사 (통계 붕괴): 트랜잭션
T1은 같은 공간에서 똑같은 조건으로 검색했는데 결과의 **개수(레코드 수)**가 달라져 버려, 평균이나 총합을 구하는 집계 보고서가 완전히 엉망진창이 됩니다.
Ⅲ. 왜 못 막았는가? (기존 자물쇠의 한계)
- 204번에서 모순성을 막으려고 **"내가 읽은 데이터(3명)에 수정 금지 자물쇠(Shared Lock)를 꽉 채워라!"**고 방어막(Repeatable Read 수준)을 쳤습니다.
- 하지만 3명의 옷 색깔(값)은 못 바꾸게 꽁꽁 묶어놨지만, 아직 존재하지도 않는 4번째 새로운 허공의 공간(빈칸)에는 애초에 자물쇠를 채울 물리적 실체가 없었습니다! 그래서 해커(T2)가 그 텅 빈 공간에 4번째 데이터를 쏙 찔러넣는(INSERT) 것을 속수무책으로 당한 것입니다.
Ⅳ. 유령을 퇴치하는 궁극의 흑마법: Serializable (직렬화) 격리 수준 🌟
이 유령을 잡으려면 DB의 성능을 반토막 내는 미친 도끼를 꺼내야 합니다.
- 방어 로직 (테이블 락 / 넥스트 키 락):
- 감사팀(
T1)이 "1천만 원 이상 가져와!"라고 외치는 순간, 데이터 3개에만 자물쇠를 거는 게 아닙니다. - 아예 엑셀 테이블(Table) 전체 문짝에 거대한 대형 자물쇠(Table Lock)를 걸거나, 1천만 원 이상이 될 수 있는 '모든 허공의 빈 공간 범위(Range Lock)' 전체를 통째로 봉쇄해 버립니다.
- 감사팀(
- 결과: 영업팀 직원(
T2)이 신규 1천만 원짜리 고객을 추가(INSERT)하려 현관문을 열려고 해도 거대한 자물쇠에 가로막혀T1이 끝날 때까지 영원히 밖에서 대기(Wait)해야 합니다. 유령은 절대 방 안으로 들어오지 못해 무결성이 완벽히 지켜집니다 (Serializable 격리). - 부작용: 문을 통째로 닫아버리니 새로운 회원 가입이나 글쓰기가 1초 동안 멈춰 동시 접속자(성능)가 개박살 납니다. 성능과 100% 무결성의 가장 극단적인 트레이드오프입니다.
📢 섹션 요약 비유: **유령 읽기(Phantom Read)**는 심야의 텅 빈 극장(데이터베이스)에서 벌어지는 **'소름 돋는 무단침입 공포 영화'**입니다. 극장 경비원(트랜잭션 T1)이 플래시를 비추며 1열 관객 수를 셉니다. "음, 1열에 3명 앉아있군."(1차 조회). 이 3명이 자리를 바꾸거나 옷을 갈아입지 못하도록 손발을 의자에 꽁꽁 묶어놨습니다(기존 데이터 잠금, Repeatable Read). 경비원이 잠시 고개를 돌린 사이, 밖에서 늦게 온 관객 1명(트랜잭션 T2)이 몰래 들어와 1열의 텅 빈 빈자리에 스르륵 앉습니다(새로운 데이터 INSERT 완료). 경비원이 1분 뒤 1열에 플래시를 다시 비췄더니, 방금 전엔 분명히 3명이었는데 아무 소리도 없이 관객이 4명으로 늘어나 있습니다! 경비원은 빈자리에 앉은 귀신(유령)을 본 것처럼 기겁하며(통계 데이터 붕괴) 멘탈이 나갑니다. 이 무서운 폴터가이스트 현상을 원천 차단하는 유일한 방법인 Serializable(직렬화) 격리 수준은, 경비원이 의자에 앉은 3명만 묶는 게 아니라 아예 **'극장 1열로 들어오는 철문 통로 전체에 거대한 바리케이드(범위 잠금)'**를 쳐버리는 것입니다. 밖에서 새로운 관객이 들어오고 싶어도 경비원이 1열 점호를 100% 끝내고 바리케이드를 치워줄 때까지 밖에서 무한 대기해야 하므로, 극장 안에 유령이 스며들 1%의 공간마저 허락하지 않는 가장 완벽하지만 가장 꽉 막힌 철통 방어 구역입니다.