낙관전 Lock은 @Version을 사용하는데, 버전이 다르면 rollback시키기 떄문에 완벽한 조회수 증가를 얻을 수 없다.
만약 동시성 문제가 자주 발생하지 않고 롤백후 에러메세지 출력이 가능한 비즈니스라면 낙관적 Lock을 이용해도 되겠다.
Lcok 성능
Lock을 이용하여 동시성 문제는 해결했지만, 필연저긍로 성능저하가 발생하게 된다. 각 방법들의 성능을 비교해보자
성능 비교방법은 각 방법별로 수행시간으로 비교해보겠다.(조회수 10000번)
동시성 고려 X
Update
비관적 Lock
동시성을 고려하지 않았을 떄 가장 빠르고, Update문이 비관적 Lock보다는 빠른것을 볼 수 있다.
성능 개선
동시성 제어는 위의 방법들을 통해 어느정도 해결하였다. 하지만 동시성제어를 근본적으로 Lock을 통해 해결하다보니 조회서비스에 성능적인 이슈가 발생할 가능성이 높다.
이를 해결하기 위해 두가지 측면으로 개선포인트를 잡아볼 수 있다.
업데이트 성능
조회와 업데이트 결합도 감소 위의 관점에서 개선을 진행해보려 한다.
업데이트 성능
업데이트가 자주 일어나서 성능 이슈가 발생할 가능성이 높다면, 업데이트를 덜 하면 된다는 아이디어이다. 즉, 조회수를 1씩 증가시키는게 아닌 10 혹은 100씩 증가시키거나, 일정한 주기별로 증가시켜주는 방법이다.하지만, 이는 내부 변수에 대한 동시성 제어가 일어나야하므로, Thread Safe한 변수를 활요하는것이 좋다.
또한 더욱 간단하게 redis같은 서비스를 이용하여 해결하는 방법도 있다. 기본적으로 redis는 단일 Thread이므로 동시성 제어에 대해 고민할 필요가 없고, 일반 데이터베이스에 비해 in/out속도가 월등히 빠르니 좋은 해결방법이 될 것이다.
CQRS
위 조회수 증가 로직들은 업데이트에서 락이 결리면 주 서비스는 조회에도 성능 이슈가 발생한다. 이를 위해 조회 서비스와 데이터에 변경이 가해지는 서비스들을 분리하여 실행하는 CQRS기법을 사용해 볼 수 있다.
카프카나 rabbitmq등의 메시지 서비스를 이용하여 조회후 업데이트 이벤트를 발행하고, 이 이벤트를 구독하는 업데이트 서비스를 통해 조회와 업데이트 로직을 분리하여 둘의 결합도를 낮추는 방법이다.
Spring에도 Event발행을 통해 해당 기능을 어느정도 구현할 수 있다.
위의 방법들 중 조회수를 10마다 증가시키는 방법과, Spring의 Event를 활용한 방법으로 서비스를 구현해보자
우선 Event클래스를 정의해주고
1 2 3 4 5 6 7 8 9
@Getter publicclassCountEventextendsApplicationEvent { privatefinal Long id;