
PostgreSQL의 FOR UPDATE 잠금은 트랜잭션 내에서 SELECT 쿼리를 수행하는 동안 테이블의 행을 명시적으로 잠그는 데 사용됩니다. 이 잠금 모드는 일반적으로 트랜잭션이 완료될 때까지 선택한 행이 변경되지 않도록 하여 다른 트랜잭션이 충돌하는 방식으로 해당 행을 수정하거나 잠그지 못하도록 하려는 경우에 사용합니다.
예를 들면 티켓 예매처럼 특정 고객이 티켓 예매 과정을 진행하는 동안 다른 고객이 데이터를 변경할 수 없도록 막기 위해 사용할 수 있어요.
이번 글에서 살펴볼 케이스들은 조금 특별합니다.
select for update 는 어떻게 동작할까요?PostgreSQL 에서 select for update 구문은 트랜잭션 격리레벨에 따라서 다르게 동작합니다. 따라서 격리레벨 별로 살펴봐야 해요.
아래와 같은 데이터가 존재할 때 데이터를 변경하는 상황을 가정해볼게요.
| id | name |
|---|---|
| 1 | null |
select for update 로 행을 잠그더라도 B 트랜잭션에서 데이터를 읽을 수 있다.
B transationDBA transationB transationDBA transation#mermaid-1713781847804{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-1713781847804 .error-icon{fill:#552222;}#mermaid-1713781847804 .error-text{fill:#552222;stroke:#552222;}#mermaid-1713781847804 .edge-thickness-normal{stroke-width:2px;}#mermaid-1713781847804 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1713781847804 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1713781847804 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1713781847804 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1713781847804 .marker{fill:#333333;stroke:#333333;}#mermaid-1713781847804 .marker.cross{stroke:#333333;}#mermaid-1713781847804 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-1713781847804 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-1713781847804 text.actor>tspan{fill:black;stroke:none;}#mermaid-1713781847804 .actor-line{stroke:grey;}#mermaid-1713781847804 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-1713781847804 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-1713781847804 #arrowhead path{fill:#333;stroke:#333;}#mermaid-1713781847804 .sequenceNumber{fill:white;}#mermaid-1713781847804 #sequencenumber{fill:#333;}#mermaid-1713781847804 #crosshead path{fill:#333;stroke:#333;}#mermaid-1713781847804 .messageText{fill:#333;stroke:none;}#mermaid-1713781847804 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-1713781847804 .labelText,#mermaid-1713781847804 .labelText>tspan{fill:black;stroke:none;}#mermaid-1713781847804 .loopText,#mermaid-1713781847804 .loopText>tspan{fill:black;stroke:none;}#mermaid-1713781847804 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-1713781847804 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-1713781847804 .noteText,#mermaid-1713781847804 .noteText>tspan{fill:black;stroke:none;}#mermaid-1713781847804 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-1713781847804 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-1713781847804 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-1713781847804 .actorPopupMenu{position:absolute;}#mermaid-1713781847804 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-1713781847804 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-1713781847804 .actor-man circle,#mermaid-1713781847804 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-1713781847804 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}읽기 가능A 가 커밋되기 전까지 대기critical[갱신유실]BEGINBEGINselect where id = 1 for updateid=1, name=nullselect where id = 1id=1, name=nullupdate set name = 'B'where id = 1update set name = 'A'where id = 1commitupdate set name = 'B'where id = 1commit