212. 충돌 직렬 가능성 (Conflict Serializable) - 스케줄 직렬화 스와핑 연산 교환 충돌 연산 선행 그래프 동시성 제어
핵심 인사이트: (211번 심화) "야! 10만 명의 쿼리가 짬뽕으로 마구 섞인 비직렬 시간표가 1억 개나 있어! 이 중에서 데이터가 꼬이지 않는 '안전한 섞기 패턴(직렬 가능 스케줄)'을 무슨 수로 1초 만에 골라낼래?! 답은 '충돌(Conflict) 안 나는 놈들끼리 자리 바꾸기(Swap)' 게임이야! 두 쿼리가 서로 다른 엑셀 칸을 건드리거나, 둘 다 눈으로 읽기(Read)만 한다면 그 두 개는 순서를 앞뒤로 바꿔도 결과가 똑같아(비충돌)! 이렇게 안전한 놈들끼리만 테트리스 블록처럼 순서를 슉슉 맞바꿔봐. 그러다 어라? 처음엔 난장판으로 섞여 있던 시간표가 어느새 'T1 쫙 끝나고 ➜ T2 쫙 끝나는' 모범생 1열 종대(직렬 스케줄) 모양으로 완벽하게 맞춰졌네?! 그럼 이 시간표는 합격! 무조건 안전한 '충돌 직렬 가능' 스케줄이다!!" 짬뽕 시간표를 마술처럼 일렬로 펴버리는 수학적 순서 교환 테스트, 충돌 직렬 가능성이다.
Ⅰ. 충돌(Conflict) 연산이란 무엇인가? 🌟
수학적 자리 바꾸기(Swap) 게임을 시작하기 전에 '절대 순서를 바꾸면 안 되는' 철칙을 알아야 합니다.
- 조건: 두 개의 트랜잭션 연산이 **1) 완벽하게 똑같은 데이터(예: 통장 A)**에 접근하고, 2) 둘 중 적어도 하나 이상이 쓰기(Write, UPDATE) 연산일 때, 두 연산을 **'충돌 연산(Conflict Operation)'**이라고 부릅니다.
- 3대 충돌 쌍 (순서 바꾸면 지옥 열림):
Read(A)와Write(A): 내가 읽기 전에 딴 놈이 쓰느냐, 쓴 후에 읽느냐에 따라 값이 천지 차이입니다.Write(A)와Read(A)Write(A)와Write(A): 누가 마지막에 덮어쓰느냐에 따라 최종 생존자가 달라집니다 (갱신 손실 위험).
- 절대 비충돌 (자리 바꿔도 OK):
Read(A)와Read(A): 둘 다 눈으로 보기만 하니 순서를 백날 바꿔도 데이터는 그대로입니다. (교환 가능)Write(A)와Write(B): 서로 다른 엑셀 칸(A와 B)을 건드리므로 순서가 상관없습니다. (교환 가능)
Ⅱ. 충돌 직렬 가능 (Conflict Serializable)의 개념 🌟
- 개념: 마구 섞인 짬뽕 비직렬 스케줄
S1이 있을 때, '충돌하지 않는 비충돌 연산(Read-Read, 다른 데이터 접근)'들의 순서를 이리저리 맞바꾸는(Swap) 조작만을 연속적으로 수행하여, 하나의 깔끔한 1열 종대 직렬 스케줄S2(예: T1 ➜ T2)로 완벽하게 변환시킬 수 있다면, 이 원래의 짬뽕 스케줄S1을 **'충돌 직렬 가능한 스케줄'**이라고 부릅니다. - 의미: 이렇게 변환에 성공했다는 것은, 이 짬뽕 시간표가 겉보기엔 막 섞여 있어도 데이터 일관성을 단 1%도 파괴하지 않는 '100% 완벽하게 안전한 병행 스케줄(합격품)'이라는 수학적 증명이 끝난 것입니다.
Ⅲ. 어떻게 검증할까? (선행 그래프 테스트) 🌟 기출 🌟
자리 바꾸기 게임을 10만 줄짜리 쿼리로 손으로 할 순 없습니다. DB 엔진은 **'선행 그래프(Precedence Graph / 직렬화 그래프)'**라는 그물망을 1초 만에 그려서 검사합니다.
- 그래프 그리기 (노드와 간선):
- 각 트랜잭션(T1, T2)을 동그라미(노드)로 그립니다.
- 시간표를 쭉 보다가, T1이 먼저
Write(A)를 때리고 나중에 T2가Read(A)를 때리는 등 '충돌 연산'이 발견되면, 먼저 한 놈(T1)에서 나중에 한 놈(T2) 쪽으로 화살표(간선 ➜)를 쫙! 긋습니다. (순서를 거스를 수 없다는 뜻)
- 합격 판정 (사이클 검사):
- 화살표를 다 그렸습니다. 만약
T1 ➜ T2화살표가 그려졌는데, 다른 충돌 때문에 또T2 ➜ T1으로 화살표가 돌아오는 '회전하는 쳇바퀴 사이클(Cycle)'이 생겼다?! - 사형!: 사이클이 발생하면 "어휴 ㅆㅂ 이거 순서가 완전히 꼬여서 영원히 직렬 스케줄로 성형을 못 하네!" 라며 충돌 직렬 불가능(데이터 붕괴 위험 스케줄) 판정을 내리고 해당 트랜잭션을 강제 롤백시켜버립니다.
- 합격!: 만약 그래프에 빙빙 도는 사이클이 하나도 없고 일방통행으로 쫙쫙 뻗어있다면? "축하합니다! 충돌 직렬 가능입니다! 마음껏 섞어서 병행 실행하십쇼!" 도장을 찍습니다.
- 화살표를 다 그렸습니다. 만약
Ⅳ. 현대 DB와의 관계
- 실제 오라클 같은 DB는 매번 그래프를 그리는 게 귀찮습니다.
- 그래서 216번 문서에 나올 **"2단계 락킹 프로토콜(2PL)"**이라는 쇠사슬 규칙을 씁니다. "야! 귀찮게 사이클 검사하지 말고, 무조건 2PL 쇠사슬 규칙대로만 락(Lock) 걸고 풀어! 2PL 규칙만 지키면 수학적으로 무조건 100% 충돌 직렬 가능한 안전한 시간표만 튀어나오게 증명되어 있으니까!"
📢 섹션 요약 비유: **충돌 직렬 가능성(Conflict Serializable)**은 난장판으로 섞여 있는 루빅스 큐브(비직렬 스케줄)를 **'규칙에 맞게 돌려서 예쁜 단색(직렬 스케줄)으로 맞추는 테트리스 게임'**입니다. 시간표에 수만 개의 쿼리 조각들이 섞여 있습니다. DB 엔진은 이 조각들의 자리를 앞뒤로 맞바꿉니다(Swap). 단, '같은 데이터를 건드리면서 하나라도 수정(Write)하는 충돌 조각들'은 본드(충돌 연산 룰)로 찰싹 붙어있어서 절대 자리를 바꿀 수 없습니다. 엔진이 본드로 안 붙어 있는 '읽기 전용 조각(Read-Read)'이나 '서로 딴 동네 건드리는 조각'들만 쇽쇽 위아래로 자리를 바꾸며 퍼즐을 맞춥니다. 그렇게 이리저리 바꿨더니, 어라? 처음엔 엄청나게 섞여 있던 큐브가 1번 큐브 쫙 모이고 2번 큐브 쫙 모인 깔끔한 1열 종대 모양(직렬 스케줄)으로 완벽하게 탁! 맞춰졌습니다! 이 순간 DB 엔진은 만세를 부릅니다. "오예! 본드(충돌 연산)의 순서를 1도 거스르지 않고 자리를 바꿔서 깔끔하게 정렬(직렬화) 시켰다는 건, 처음 섞여 있던 그 짬뽕 시간표가 겉으론 개판이어도 데이터 모순(에러)을 단 한 방울도 일으키지 않는 100% 완벽하게 안전한 황금 섞기 조합(충돌 직렬 가능 스케줄)이었다는 완벽한 증명이다!"