개발기록
데이터베이스 쓰기 작업과 락킹: 언제 필요하고 언제 필요하지 않은가 본문
많은 개발자들이 데이터베이스 쓰기 작업을 할 때 항상 명시적인 락킹(낙관적 또는 비관적)이 필요하다고 오해하곤 합니다.
하지만 실제로는 그렇지 않습니다. 여기서 이 개념을 명확히 해보겠습니다.
기본 원칙
기본적인 쓰기 작업은 락이 필요하지 않습니다.
- 데이터베이스 관리 시스템(DBMS)은 기본적으로 트랜잭션 격리와 동시성 제어를 자체적으로 관리합니다.
ACID 속성은 기본적으로 보장됩니다.
- 트랜잭션의 원자성(Atomicity), 일관성(Consistency), 격리성(Isolation), 지속성(Durability)은 DBMS가 보장합니다.
언제 추가적인 락킹이 필요한가?
동시성 문제가 예상되는 경우
- 여러 트랜잭션이 동시에 같은 데이터를 수정하려 할 때
장기 실행 트랜잭션
- 트랜잭션이 오랜 시간 동안 실행되어 다른 트랜잭션과 충돌할 가능성이 높을 때
비즈니스 로직 상 데이터 일관성이 매우 중요한 경우
- 예: 은행 계좌 이체, 재고 관리 등
예시
1. 락이 필요 없는 일반적인 쓰기 작업
@Service
@Transactional
class UserService(private val userRepository: UserRepository) {
fun updateUserName(userId: Long, newName: String) {
val user = userRepository.findById(userId).orElseThrow()
user.name = newName
userRepository.save(user)
}
}
이 경우, 명시적인 락킹 메커니즘 없이도 DBMS가 트랜잭션을 안전하게 처리합니다.
2. 낙관적 락이 필요한 경우
@Entity
class Product(
@Id val id: Long,
var name: String,
var stock: Int,
@Version var version: Long
)
@Service
@Transactional
class ProductService(private val productRepository: ProductRepository) {
fun decreaseStock(productId: Long, quantity: Int) {
val product = productRepository.findById(productId).orElseThrow()
if (product.stock >= quantity) {
product.stock -= quantity
productRepository.save(product)
} else {
throw IllegalStateException("Not enough stock")
}
}
}
여기서 @Version 필드는 낙관적 락을 구현합니다. 동시에 여러 트랜잭션이 재고를 수정하려 할 때 유용합니다.
3. 비관적 락이 필요한 경우
@Repository
interface ProductRepository : JpaRepository<Product, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT p FROM Product p WHERE p.id = :id")
fun findByIdWithLock(@Param("id") id: Long): Product?
}
@Service
@Transactional
class ProductService(private val productRepository: ProductRepository) {
fun decreaseStock(productId: Long, quantity: Int) {
val product = productRepository.findByIdWithLock(productId) ?: throw NoSuchElementException()
if (product.stock >= quantity) {
product.stock -= quantity
productRepository.save(product)
} else {
throw IllegalStateException("Not enough stock")
}
}
}
이 방식은 동시성 문제가 자주 발생하고, 데이터 일관성이 매우 중요한 경우에 사용합니다.
결론
- 모든 쓰기 작업에 명시적인 락킹이 필요한 것은 아닙니다.
- DBMS의 기본 동시성 제어 메커니즘을 신뢰하세요.
- 특별한 동시성 요구사항이 있는 경우에만 추가적인 락킹 메커니즘을 고려하세요.
- 락킹은 성능에 영향을 줄 수 있으므로, 꼭 필요한 경우에만 사용하세요.
적절한 락킹 전략 선택은 애플리케이션의 요구사항, 예상되는 동시성 수준, 그리고 성능 고려사항을 바탕으로 이루어져야 합니다.
Comments