해당 포스팅은 인프런 최창용님의 '재고시스템으로 알아보는 동시성이슈 해결방법'을 학습 후 정리한 내용입니다.
멀티쓰레드 환경에서 둘 이상의 쓰레드가 공유 자원에 접근하려 할 때, 그 공유 자원의 최종 상태가 쓰레드 실행 순서에 따라 달라지는 상황을 말합니다.
문제
예를 들어, 하나의 변수를 여러 쓰레드가 동시에 읽고 쓰기를 시도한다면, 각 쓰레드가 읽고 쓰는 순서에 따라 변수 값이 예상치 못한 결과를 가져올 수 있습니다. 이러한 상황이 발생하면 프로그램의 정확성을 보장할 수 없으며, 디버깅이 어려워집니다.
해결방법
synchronized는 자바에서 동시성 문제를 해결하기 위해 제공되는 키워드 중 하나로, 멀티쓰레드 환경에서 동기화를 제공합니다.
여러 쓰레드가 동시에 하나의 객체에 접근할 때, 동기화 없이 동시에 접근하면 예측할 수 없는 결과가 발생할 수 있습니다. 따라서 synchronized를 사용하여 객체에 대한 여러 개의 쓰레드의 접근을 동기화시키면, 하나의 쓰레드가 객체에 접근하고 수정하는 동안 다른 쓰레드들은 대기하게 됩니다.
@Transactional은 스프링 프레임워크에서 제공하는 어노테이션 중 하나로, 메소드 실행 시 트랜잭션을 적용하기 위해 사용됩니다. 트랜잭션은 데이터베이스의 상태를 변화시키는 작업을 모두 하나의 논리적인 작업 단위로 묶어서 실행시키기 때문에, 데이터 무결성과 일관성을 유지할 수 있습니다.
@Transactional 어노테이션을 사용하면 메소드 실행 시 자동으로 트랜잭션을 시작하고, 메소드가 정상적으로 종료될 때 트랜잭션을 커밋합니다. 만약 메소드 실행 중 예외가 발생하면, 트랜잭션을 롤백합니다. 이를 통해 개발자는 직접 트랜잭션을 관리하지 않고도 간편하게 데이터베이스 작업을 수행할 수 있습니다.
@Transactional 어노테이션은 클래스나 메소드에 적용할 수 있으며, 클래스에 적용하면 해당 클래스의 모든 메소드에 트랜잭션을 적용할 수 있습니다. 기본적으로 @Transactional은 읽기 작업에는 적용되지 않습니다. 만약 읽기 작업에도 트랜잭션을 적용하고 싶다면 readOnly 속성을 true로 설정해야 합니다.
@Transactional(readOnly = true)
public void readData() {
// ...
}
**@Transactional을 사용할 때는 올바른 격리 수준을 설정하고, 최적의 트랜잭션 관리 방식을 선택하는 것이 중요합니다.**
@Transactional 어노테이션은 메서드에서 실행되는 모든 데이터베이스 작업을 트랜잭션으로 묶어줍니다. 이는 여러 개의 동시 요청이 같은 트랜잭션에 영향을 미칠 수 있다는 것을 의미합니다.
따라서 synchronized 키워드와 함께 사용될 경우 동시성 이슈를 완전히 해결하지 못할 수 있습니다.
동시성 이슈를 해결하려면 @Transactional 어노테이션의 격리 수준을 높여 동시성 이슈를 해결할 수 있습니다. 기본적으로 스프링은 READ_COMMITTED 격리 수준을 사용합니다. 이를 높이면 트랜잭션 범위 내에서 다른 쓰레드의 작업을 차단하여 동시성 이슈를 해결할 수 있습니다.
@Transactional(isolation = Isolation.SERIALIZABLE)
public void doSomething() {
// ...
}
동시성 이슈 발생
@Transactional 어노테이션은 트랜잭션 범위 내에서 다른 쓰레드에게 접근을 허용하므로, synchronized 키워드와 함께 사용될 경우 동시성 이슈를 완전히 해결하지 못할 수 있습니다.
따라서, synchronized 키워드와 @Transactional 어노테이션을 함께 사용하는 것은 권장되지 않습니다. 대신, 데이터베이스에서 적절한 동시성 제어 방법을 사용하거나, @Transactional 어노테이션의 격리 수준을 높여 동시성 이슈를 해결할 수 있습니다.
해결방법
synchronized키워드는 메서드 전체에 적용되어 있지만, 필요한 부분만 synchronized블록으로 변경하면 성능 개선을 할 수 있습니다
@Transactional
public synchronized void decrease(Long id, Long quantity) {
//실행메소드
}
Race Condition해결해보기Optimistic Lock (낙관적인 락)Pessimistic Lock (exclusive lock) (비관적인 락)named Lock 활용하기참고문헌
https://dev.mysql.com/doc/refman/8.0/en/glossary.html#glos_exclusive_lock
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
https://dev.mysql.com/doc/refman/8.0/en/locking-functions.html