Pink Spider/JPA사용시 row level lock 적용

Created Thu, 24 Jul 2025 10:07:45 +0900 Modified Mon, 08 Dec 2025 08:41:47 +0900
925 Words 4 min

Spring Boot 3 + JPA + MySQL 환경에서 특정 테이블에 대해 findsave 작업 시 row-level lock만 사용하고 table-level lock을 피하고 싶을 경우, 다음과 같은 전략을 사용할 수 있습니다.


✅ 핵심 개념 요약

  • MySQL InnoDB 스토리지 엔진은 기본적으로 row-level lock을 지원하며, JPA의 기본 동작은 row-level lock을 사용합니다.

  • 그러나 다음 조건을 만족하지 않으면 innoDB라도 table lock이 발생할 수 있습니다:

    • WHERE 절에 인덱스를 잘 사용하지 못하거나
    • 락 모드가 명시되지 않았거나
    • UPDATESELECT ... FOR UPDATE에서 범위 검색을 제대로 구성하지 않았거나

✅ 전제 조건: 테이블이 InnoDB 엔진인지 확인

SHOW TABLE STATUS WHERE Name = 'your_table_name';
  • Engine 컬럼이 InnoDB여야 row-level lock이 적용됩니다.
  • 만약 MyISAM이면 row-level lock이 불가능하므로 반드시 InnoDB로 변경해야 합니다:
ALTER TABLE your_table_name ENGINE = InnoDB;

✅ JPA에서 row-level locking만 적용하기

1. @Lock(LockModeType.PESSIMISTIC_WRITE) 또는 @Lock(LockModeType.PESSIMISTIC_READ)

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("SELECT o FROM Order o WHERE o.id = :id")
    Optional<Order> findByIdForUpdate(@Param("id") Long id);
}
  • PESSIMISTIC_WRITESELECT ... FOR UPDATE를 실행하여 row-level exclusive lock을 겁니다.
  • 이 쿼리는 해당 row를 다른 트랜잭션에서 변경하지 못하게 막습니다.
  • PESSIMISTIC_READ는 공유락을 걸며 읽기는 허용하되 쓰기는 막습니다.

⚠️ 주의: 이 방법은 반드시 트랜잭션(@Transactional) 안에서 사용되어야 실제로 락이 적용됩니다.


2. 트랜잭션 범위 명확하게 잡기

@Service
public class OrderService {

    @Transactional
    public void updateOrder(Long orderId) {
        Order order = orderRepository.findByIdForUpdate(orderId)
                                     .orElseThrow(...);
        order.setStatus("SHIPPED");
        orderRepository.save(order);
    }
}
  • 트랜잭션 안에서 find + update 작업을 하면 row-level lock이 보장됩니다.
  • 여러 row를 다루더라도, 쿼리가 적절한 인덱스를 사용하면 table lock 없이 작동합니다.

✅ row-level lock만 보장되도록 주의할 점

📌 인덱스를 반드시 활용하라

  • WHERE 조건에 Primary Key 또는 Unique Index를 활용하지 않으면 MySQL이 full table scan 후 table-level lock을 걸 수도 있습니다.

📌 범위 검색시 FOR UPDATE 주의

  • BETWEEN, <, > 등의 조건이 있을 경우 MySQL이 gap lock 또는 next-key lock을 걸어버릴 수 있습니다.
  • 이를 피하려면 SELECT ... FOR UPDATE에서 정확한 key 조건을 사용하세요.

✅ 참고: Optimistic Lock vs Pessimistic Lock

  • Optimistic Lock: @Version 필드를 이용, 충돌시 예외 발생. 락 자체는 없음.
  • Pessimistic Lock: 위에서 설명한 @Lock(LockModeType.XXX) 방식. row에 DB 락이 걸림.

🧪 예시 Entity

@Entity
public class Order {
    @Id
    private Long id;

    private String status;

    @Version
    private Integer version; // Optimistic Lock용
}

✅ 요약

목적 적용 방식
row-level lock만 사용 JPA에서 @Lock(LockModeType.PESSIMISTIC_WRITE) + @Transactional 사용
table-level lock 방지 InnoDB 사용 + PK 또는 인덱스를 통한 WHERE 조건 구성
락 충돌 방지 비즈니스 요구에 따라 Pessimistic 또는 Optimistic Lock 병행 고려