쏭의 개발 블로그
비관적 락(Pessimistic Lock) vs 낙관적 락(Optimistic Lock) 본문
[1] 동시성 제어
- 여러 사용자가 동시에 데이터에 접근할 때 데이터의 일관성과 무결성을 유지하기 위해 필수적이다.
- 이를 통해 데이터 충돌을 방지하고 데이터 손실이나 오류 없이 시스템 안정성과 신뢰성을 확보할 수 있다.
- 동시성 제어 전략 : 비관적 락, 낙관적 락
[2] 비관적 락 (Pessimistic Lock)
데이터 접근 시에 항상 충돌이 발생할 가능성이 있다고 가정하고, 데이터를 보호하가 위해 항상 락을 걸어 다른 트랜잭션 접근을 방지한다.
- 데이터의 변경이 발생할 가능성을 미리 차단하여 데이터의 일관성을 보장
- 다른 트랜잭션들은 락이 해제되기까지 대기한다. 락을 오래 점유하고 있으면 성능 저하나 deadlock 등으로 인한 장애 문제가 발생할 수 있다.
- 비관적 락을 사용할 때는 락의 범위와 지속 시간을 신중하게 관리해야한다.
- Record Lock은 비관적 락이다.
장단점
장점
- 데이터의 무결성을 완벽히 지킬 수 있다. (수정할 데이터에 row level lock을 걸기 때문에 다른 요청에서 수정하는 것은 불가능)
- 충돌 발생 가능성이 적다.
- 구현이 비교적 간단하다
단점
- lock으로 인하여 이후의 다른 요청은 대기 상태로 빠진다.
- 락을 걸고 해제하는 과정에서 많은 자원이 소모되어 성능 저하를 초래할 수 있다
- 다른 트랜잭션의 접근을 차단하므로 병목 현상이 발생할 수 있다.
사용
- 데이터의 충돌 가능성이 높고, 데이터의 일관성을 유지해야하는 중요한 작업
- 예시 : 금융 시스템, 재고 관리 시스템, 예약 시스템 등
- 은행 계좌 잔액 업데이트 작업 : 비관적 락을 사용하여 동시에 여러 트랜잭션이 같은 계좌에 접근하는 것을 방지
구현 방법
(1) update
- 데이터베이스에 저장된 데이터 기준으로 update문 수행한다.
- Exclusive Lock(쓰기 락)으로 다른 트랜잭션은 이 row를 읽거나 쓸 수 없다.
- 데이터의 기존 값을 확인할 필요 없이, 단순히 값을 증가/변경하는 경우에 적합하다.
-- 트랜잭션 시작
transaction start;
-- 좋아요 데이터 삽입
insert into article_like
values({article_like_id}, {article_id}, {user_id}, {created_at});
-- 좋아요 수 데이터 갱신 (Pessimistic Lock 점유)
update article_like_count
set like_count = like_count + 1
where article_id= {article_id};
-- Pessimistic Lock 해제
commit;
- 락 점유 시점 : UPDATE 실행 시점
- 락 점유 시간 : UPDATE 시 바로 점유하고 COMMIT까지 유지하므로 상대적으로 짧음
- 사용 : 단순한 카운트 증가, 조건 없는 갱신 작업
- 주의할 점 : UPDATE가 실제로 값을 변경하지 않더라도 락이 점유되므로 경합을 주의해야 함
(2) select for update + update
- 트랜잭션에 조회된 데이터 기준으로 update문 수행한다
- 조회 결과에 대해 락을 점유한다
- 값을 미리 확인하거나 조건에 따라 변경할지 말지를 판단해야하는 경우에 유리하다
-- 트랜잭션 시작
transaction start;
-- 좋아요 데이터 삽입
insert into article_like
values({article_like_id}, {article_id}, {user_id}, {created_at});
-- for update 구문으로 데이터 조회
-- 조회된 데이터에 대해서 Pessimistic Lock 점유(이 시점부터 다른 Lock은 점유될 수 없다.)
-- 애플리케이션에서 JPA를 사용하는 경우, 객체(엔티티)로 조회할 수 있다.
select * from article_like_countwhere article_id= {article_id} for update;
-- 좋아요 수 데이터 갱신
-- 조회된 데이터를 기반으로새로운 좋아요 수를 만들어준다. (조회 시점부터 Lock을 점유하고 있기 때문에 가능)
-- Client(애플리케이션)에서 JPA를 사용하는 경우, 엔티티로 위 과정을 수행할 수 있다.
update article_like_countset like_count= {updated_like_count} where article_id= {article_id};
-- Pessimistic Lock 해제
commit;
- 락 점유 시점 : SELECT FOR UPDATE 실행 시점
- 락 점유 시간 : SELECT 시점부터 트랜잭션 종료까지이므로 상대적으로 길다.
- 사용 : 현재 값 기반으로 계산이 필요한 경우
- 주의할 점 : 락 점유 시간이 길어 경합, 데드락 가능성⬆️
[3] 낙관적 락 (Optimistic Lock)
데이터 접근 시에 항상 충돌이 발생할 가능성이 없다고 가정하rh, 데이터의 변경 여부를 확인하고 충돌을 처리한다.
- 데이터가 다른 트랜잭션에 의해 수정되었는지 확인하고, 수정된 내역이 있으면 rollback이나 재처리를 수행한다.
- 데이터의 변경 여부는 각 테이블의 version 컬럼으로 데이터 변경 여부를 추적한다.
- 충돌을 감지하고 후처리를 위한 추가 작업이 필요하다. 충돌 발생 시 commit, rollback, 재시도를 수행해야하며, 이는 애플리케이션에서 구현해야한다.
장단점
장점
- 성능이 향상될 수 있다
- 다른 트랜잭션의 접근을 차단하지않으므로 병목 현상이 발생하지 않는다.
- 락을 걸고 해제하는 과정에서 오버헤드가 발생하지 않는다.
단점
- 데이터의 일관성을 보장하기 어렵다.
- 충돌이 발생할 가능성이 있다.
- 구현이 비교적 복잡하다.
사용
- 충돌 가능성이 낮은 환경
- 시스템의 성능을 최대한 유지해야하는 경우
- 예시 : 소셜 네트워크, 블로그 시스템 ,전자 상거래 시스템 등
- 소셜 미디어의 사용자 프로필 업데이트 : 동시에 많은 사용자가 데이터를 수정하지 않는 경우
작동 흐름
- 데이터 조회 (Read + Version 확인)
- 트랜잭션 시작 시점에 레코드와 함께 version을 조회
- version : 해당 레코드가 마지막으로 수정된 시점
- 데이터 수정 시도
- 애플리케이션은 조회했던 version 값을 기반으로 데이터를 수정하려고 합니다.
- UPDATE 시 WHERE 절에 version = 조회했던 값 조건을 포함합니다.
- 충돌 여부 검증
- 데이터 변경 성공 : 충돌❌
- 조회한 시점 이후로 다른 트랜잭션이 데이터를 변경하지 않았으면 version이 그대로이므로 수정이 성공. 이 때 version은 자동 증가
- 데이터 변경 실패 : 충돌⭕
- 다른 트랜잭션이 먼저 데이터를 수정하여 version이 변경되었다면 조건이 맞지 않아 UPDATE는 0건 수행되고, 예외가 발생하거나 실패로 처리
- 데이터 변경 성공 : 충돌❌
구현 방법
- version 컬럼을 추가하고 애플리케이션에서 낙관적 락에 의한 충돌 처리 작업이 필요
- JPA에서는 테이블에 버전 관리를 위한 컬럼을 추가하고 @Version 애노테이션을 달아주면 알아서 처리해준다.
사전 준비
테이블에 version 컬럼을 추가
ALTER TABLE article_like_count ADD COLUMN version INT DEFAULT 0;
트랜잭션 흐름
-- 트랜잭션 시작
BEGIN;
-- 현재 버전 조회
SELECT version FROM article_like_count WHERE article_id = 123;
-- 애플리케이션 로직 수행 (version 번호 조건)
-- 업데이트 시 version 조건 추가
UPDATE article_like_count
SET like_count = like_count + 1,
version = version + 1
WHERE article_id = 123 AND version = 5;
-- 커밋
COMMIT;
비관적 락 vs 낙관적 락
비관적 락 | 낙관적 락 | |
개념 | 데이터 접근 시 즉시 락을 걸어 다른 트랜잭션의 접근 차단 | 락을 걸지 않고 트랜잭션 종료 시 충돌 여부 확인 |
충돌 처리 방식 | 충돌을 사전에 방지 | 충돌을 사후에 검증하고 처리 |
성능 | 동시성 저하 가능성 | 락 오버헤드 적어 성능에 유리 |
일관성 보장 수준 | 일관성이 높아 데이터 정합성을 강력 보장 | 일관성이 낮을 수 있으므로 충돌 발생시 처리 필요 |
장점 | 충돌 가능성이 높은 환경에서 안전하게 데이터를 보호 | 충돌이 적은 환경에서 높은 성능 발휘 (높은 동시성) |
단점 | 락 경합, 데드락 위험, 성능 저하 가능 | 충돌 시 재시도 로직 필요 |
사용 | 데이터 경합이 심하고 정합성이 중요한 경우 | 읽기 중심이거나 충돌이 적은 웹 시스템 |
- 비관적 락 : 충돌 가능성이 높거나 정합성 오류가 심각한 결과를 초래하는 경우
- 낙관적 락 : 충돌 가능성이 낮거나 읽기 작업이 높을 때, 성능이 중요하고 재시도가 허용 가능한 로직일 때
참고 자료
https://minjooig.tistory.com/147
https://chrisjune-13837.medium.com/db-동시성-문제-해결방법-f5e52e2e3
https://f-lab.kr/insight/pessimistic-lock-vs-optimistic-lock-20240707
'DB' 카테고리의 다른 글
PostgreSQL에서의 JSON 저장 (0) | 2025.06.01 |
---|---|
Redis 데이터 백업 전략 (RDB, AOF, AOF-RDB Hybrid) (0) | 2025.05.18 |
Windows 환경에서 Docker에 Redis 설치 (0) | 2025.03.30 |
Index Scan 인덱스 스캔 (0) | 2025.03.23 |
DB 커넥션 풀의 용도와 필요 (0) | 2025.03.16 |