
관계형 데이터베이스는 트랜잭션의 격리성(Isolation)을 보장하기 위해 데이터에 락을 걸어 다른 트랜잭션이 접근하지 못하도록 합니다.
update 재고 -1이 먼저 실행되는 경우
product_id = 123인 로우)에 배타적 락(Exclusive Lock)이 걸립니다.update 재고 -1이 마지막에 실행되는 경우
특히 여러 트랜잭션이 같은 리소스(여기서는 특정 재고 로우)에 집중될 때 이 순서의 차이는 수십 배의 성능 차이로 나타날 수 있습니다.
JPA는 말씀하신 대로 영속성 컨텍스트의 더티 체킹(Dirty Checking)을 활용합니다. 엔티티의 변경 사항은 즉시 DB에 반영되지 않고, 트랜잭션이 커밋될 때(또는 flush() 호출 시) 한 번에 모아서 SQL을 실행합니다.
따라서 JPA 환경에서는 코드로 product.setStock(product.getStock() - 1)를 호출하더라도, 실제로 UPDATE 쿼리는 트랜잭션 커밋 직전에 실행됩니다. 이는 의도치 않게 재고 업데이트가 트랜잭션의 마지막 순서로 배치되는 효과를 가져오므로, JPA를 사용하면 자연스럽게 락 보유 시간을 최소화하는 방식이 됩니다.
하지만 이 동작 방식을 이해하지 못하면 왜 이런 성능 차이가 발생하는지 파악하기 어려울 수 있습니다.