일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- redis
- nosql
- DFS
- 운영체제
- spring
- 완전탐색
- BFS
- Algorithm
- db
- javascript
- 데이터베이스
- It
- jpa
- 자료구조
- CSS
- 트랜잭션
- OS
- HTML
- CS
- Docker
- 프로그래머스
- websocket
- Data structure
- 플로이드-워셜 알고리즘
- 알고리즘
- 영속성 컨텍스트
- java
- 백준
- PYTHON
- mysql
- Today
- Total
If at first you don't succeed, try again
[DB] 비관적 락(Pessimistic Lock) & 낙관적 락(Optimistic Lock) 본문
* 개요
저번 글에 lock에 대해 개념과 실습을 진행했었다.
이번 글에서는 비관적 락과 낙관적 락에 대해 정리할 예정이다.
예시는 좋아요 수와 관련하여 들도록 하겠다!
* 비관적 락(Pessimistic Lock)
- 비관적 락은 데이터 접근 시에 항상 충돌이 발생할 가능성이 있다고 가정한다.(비관적 관점)
- 데이터를 보호하기 위해 항상 락을 걸어 다른 트랜잭션 접근을 방지한다.
- 다른 트랜잭션은 락이 해제될 때까지 대기하는데, 락을 오래 점유하면 성능 저하 또는 deadlock으로 인한 장애가 발생한다.
- 지난 글에서 살펴봤던 record lock은 비관적 락에 해당한다.
- 비관적 락을 구현할 수 있는 방법은 크게 2가지로 나뉜다.
* 비관적 락 - 방법 1
1. 트랜잭션 시작
transaction start;
2. 좋아요 데이터 삽입
insert into article_like values(
{article_like_id}, {article_id}, {user_id}, {created_at}
);
3. 좋아요 수 데이터 갱신, 비관적 락 점유
update article_like_count set like_count = like_count + 1
where article_id = {article_id};
4. COMMIT하여 비관적 락 해제
commit;
이 방법은 데이터베이스 저장된 데이터 기준으로 업데이트를 수행한다.
그리고 업데이트 시 락을 점유한다.
* 비관적 락 - 방법 2
1. 트랜잭션 시작
transaction start;
2. 좋아요 데이터 삽입
insert into article_like values(
{article_like_id}, {article_id}, {user_id}, {created_at}
);
3. for update 구문으로 데이터 조회
select * from article_like_count
where article_id = {article_id}
for update;
- 조회된 데이터에 대해서 비관적 락 점유
- 이 시점부터 다른 락은 점유될 수 없다.
- 애플리케이션에서 JPA를 사용하는 경우, 객체로 조회할 수 있다.
4. 좋아요 수 데이터 갱신
update article_like_count set like_count = {updated_like_count}
where article_id = {article_id};
- 조회된 데이터를 기반으로 새로운 좋아요 수를 만들어준다. 이는 조회 시점부터 락을 점유하고 있기에 가능하다.
- Client에서 JPA를 사용하는 경우, 엔티티로 위 과정을 수행할 수 있다.
5. COMMIT하여 비관적 락 해제
commit;
for update 구분으로 조회 겨로가에 대해 락을 점유하겠다고 명시한다.
그리고 트랜잭션에 조회된 데이터 기준으로 update 문을 수행한다.
* 방법 1(update 구문) vs 방법 2(select for update + update 구문)
- 락 점유
- 방법 1 : update 문 수행하는 시점에 락을 점유한다. 따라서 락을 점유하는 시간이 상대적으로 짧다.
- 방법 2 : 데이터 조회 시점부터 락을 점유한다. 락을 점유하는 시간이 상대적으로 길다. 데이터를 조회한 뒤 중간 과정을 수행해야 하기 때문에, 락 해제가 지연될 수 있다.
- 애플리케이션 개발
- 방법 1 : 데이터베이스의 현재 저장된 데이터 기준으로 증감 처리하기 때문에 SQL문을 직접 전송한다.
- 방법 2 : JPA를 사용하는 경우, 엔티티를 이용하여 좀 더 객체지향스럽게 개발할 수 있다.
* 낙관적 락(Optimistic Lock)
- 낙관적 락은 데이터 접근 시에 항상 충돌이 발생할 가능성이 없다고 가정한다.(낙관적 관점)
- 데이터의 변경 여부를 확인하여 충돌을 처리한다.
- 데이터가 다른 트랜잭션에 의해 수정되었는지 확인한다.
- 수정된 내역이 있으면 후처리를 진행한다.(ROLLBACK 또는 재처리 등)
그렇다면 데이터의 변경 여부는 어떻게 확인할까?
-> 각 테이블의 version 컬럼으로 데이터의 변경 여부를 추적한다.
충돌은 어떻게 확인할 수 있을까?
1. 각 트랜잭션에서 version을 함께 조회한다.
2. 레코드를 업데이트 한다.
- 이 때, where 조건에 조회된 version을 넣고, version을 증가시킨다.
3. 충돌을 확인한다.
- 데이터 변경이 성공했다면, 충돌이 발생하지 않은것이다.
- 데이터 변경이 실패했다면, 충돌이 발생한 것이다. 다른 트랜잭션에서 version을 이미 증가시켰기 때문이다.
* 비관적 락 방법 1 적용 - 좋아요 수
테이블 article_like_count을 생성한다.
create table article_like_count (
article_id bigint not null primary key,
like_count bigint not null,
version bigint not null
);
비관적 락 방법 1은 데이터베이스에 저장된 데이터 기준 update문을 직접 수행해준다.
update article_like_count
set like_count = like_count + 1
where article_id = {article_id}
* 비관적 락 방법 2 적용 - 좋아요 수
비관적 락 방법 2는 트랜잭션에 조회된 데이터 기준 for update 구문으로 데이터를 조회한 뒤, 조회된 데이터 기반으로 좋아요 수를 갱신해준다.
select * from article_like_count
where article_id = {article_id} for update;
update article_like_count
set like_count = {updated_like_count}
where article_id = {article_id}
* 낙관적 락 방법 적용 - 좋아요 수
낙관적 락 방법은 version 컬럼을 추가하고, 애플리케이션에서 낙관적 락에 의한 충돌 처리 작업이 필요하다.
이러한 충돌 처리 작업은 단순히 ROLLBACK으로 처리한다.
충돌 시 ROLLBACK 처리는 어떻게 할까?
- JPA를 사용하지 않는 경우 : version을 조회한 다음, update 구문을 직접 작성하고 실행해야 한다.
- JPA를 사용하는 경우 : @Version 어노테이션을 추가하면 알아서 처리해준다.
'DB' 카테고리의 다른 글
[DB] Lock의 개념 및 테스트 (0) | 2025.03.28 |
---|---|
[DB] 페이징 조회 쿼리 성능 개선 - MySQL (0) | 2025.03.08 |
[DB] Index 실습(대용량 데이터 처리) - MySQL (0) | 2025.03.08 |
[DB] 트랜잭션 (0) | 2025.02.07 |
[DB] SQL의 개념 (0) | 2021.09.10 |