ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 데이터베이스 데드락, 공유락, 베타락: 꼭 알아야 할 실무 필수 개념 (JPA)
    성장과정(dev)/Spring + Java + JPA 2024. 12. 13. 16:10

    Lock 의 종류

     

    • 비관적 락(Pessimistic Lock): 항상 락을 사용. 데드락이 발생할 가능성이 높아질 수 있습니다.
    • 낙관적 락(Optimistic Lock): 데이터 충돌이 발생했을 때만 처리. 이 방식이 데드락을 방지한다고 해도 한계가 있음. 잦은 충돌 발생 시 트랜잭션 반복 재시도로 성능 저하, 트랜젝션이 길 경우 충돌 가능성 상승. 따라서 낮은 경쟁환경에서 효과적

     

     

     

    특성을 반영하면 낙관적 락, 비관적 락은 실무에서 보통 어떤 상황에 사용하나요?

    낙관적 락

    • 전자상거래 시스템에서 사용자 장바구니의 데이터 관리.
    • 게시판, 블로그 등 대부분 읽기 위주의 애플리케이션.

    비관적 락

    • 은행 거래 시스템, 재고 관리 시스템.

     

     

     

    비관적 락의 주요 유형 2가지 공유 락, 베타 락

    1. 공유 락 (Shared Lock) = 읽기 락(Read Lock)
      • 읽기 작업을 수행하는 동안 락을 설정
      • 조회한 데이터가 트랜잭션 내내 변경되지 않음을 보장
      • 여러 트랜잭션이 동시에 데이터를 읽을 수 있지만, 쓰기 작업은 제한
      • MySQL 사용 예: SELECT ... LOCK IN SHARE MODE
    2. 배타 락 (Exclusive Lock) = 쓰기 락(Write Lock)
      • 쓰기 작업을 수행하는 동안 락을 설정
      • 배타 락을 획득한 트랜잭션은 해당 데이터에 대한 독점권을 가짐
      • 하나의 트랜잭션만 데이터에 접근 가능.
      • 예: SELECT ... FOR UPDATE (MySQL)

     

     

     

    베타 락 사용 시 주로 발생하는 데드락

    데드 락(Dead Lock)이란 교착 상태로, 두개 이상의 트랜잭션이 서로 필요로 하는 데이터의 락을 점유하고 있어서 무한히 대기하는 상황.

    > 순환 대기 (Circular Wait)

    • 두 개 이상의 트랜잭션이 서로가 점유한 자원을 기다리며 원형 대기를 형성.
    • 예:
      • 트랜잭션 A: 자원 1 → 자원 2 요청 (대기)
      • 트랜잭션 B: 자원 2 → 자원 1 요청 (대기)

     

     

    데드락 어떻게 해결하나요?

    1. 트랜잭션에서 락 획득 순서를 일관되게 하기
      > 모든 트랜잭션에서 1번 데이터, 2번 데이터 순으로 락을 획득할 시 데드 락이 발생하지 않음
    2. 락 타임 아웃을 설정합니다.

    //락 타임아웃 걸기
    Map<String, Object> properties = new HashMap<>();
    
    //이건 yaml 에 정의하고 AppProperties 정의하는 걸로 대체하자
    properties.put("javax.persistence.lock.timeout", 5000); // 5초 타임아웃
    
    User user = entityManager.find(User.class, userId, LockModeType.PESSIMISTIC_WRITE, properties);

     

    Lock 의 실행주체는?

    • 일반적으로 애플리케이션에서 데이터베이스 관리 시스템(DBMS)은 데이터의 일관성과 무결성을 유지하기 위해 Lock을 설정
    • JPA에서 제공하는 Lock 기능 역시 결국 DBMS에 Lock 설정을 요청하여 동작

     

     

    JPA 에서 비관적 락 사용하기

    JPA 에서는 @Lock 어노테이션을 통해 비관적 락과 추가 세부옵션(공유 락, 배타 락 등 구현방식 설정) 지원.

    비관적 락은 주로 PESSIMISTIC_WRITE (배타 락)을 사용

    //JPA 에서의 pessimistic lock
    // PESSIMISTIC_READ, PESSIMISTIC_WRITE
    Account account = entityManager.find(Account.class, accountId, LockModeType.PESSIMISTIC_WRITE);

     

     

     

    트랜잭션에서 락 획득 순서를 일관되게 하기(JPA 에서의 예제)

    @Service
    public class UserService {
    
        @PersistenceContext
        private EntityManager entityManager;
    
        @Transactional
        public void updateUserAccount(Long userId, Long accountId, double amount, String newUserName) {
            // Account 엔티티에 배타 락 설정
            Account account = entityManager.find(Account.class, accountId, LockModeType.PESSIMISTIC_WRITE);
    
            // User 엔티티에 배타 락 설정
            User user = entityManager.find(User.class, userId, LockModeType.PESSIMISTIC_WRITE);
    
            // 비즈니스 로직: 계좌 잔액 업데이트
            double newBalance = account.getBalance() + amount;
            if (newBalance < 0) {
                throw new IllegalArgumentException("잔액이 부족합니다.");
            }
            account.setBalance(newBalance);
    
            // 비즈니스 로직: 사용자 이름 업데이트
            user.setName(newUserName);
    
            // EntityManager가 트랜잭션 종료 시점에 변경 사항을 자동으로 반영(Flush)합니다.
        }
    }

     

     

     

    JPA 에서 낙관적 락 사용하기

    // 엔티티에 @Version 필드 추가 (테이블에 version 컬럼 추가)
    
    @Entity
    public class User {
        @Id
        @GeneratedValue
        private Long id;
    
        private String name;
    
        @Version
        private Integer version;
    
        // Getter, Setter
    }

     

Designed by Tistory.