티스토리 뷰

대용량 트래픽 처리 서버를 구축할 때 동시성을 제어하는 것은 매우 중요하다.

왜냐하면 대량의 트래픽이 몰릴 때 데이터의 정합성을 유지하는 것이 필수적인 요소이기 때문이다.

 

내가 직접 개발하면서 경험한 동시성 제어 프로그래밍에 대해서 작성해본다.

 

[동시성 제어 프로그래밍 방식]

동시성 제어 프로그래밍은 트랜잭션, 트래픽, 분산 환경 여부 등에 따라 다르게 채택하여 사용할 수 있다.

콘서트 예약시스템에서 동시성 이슈가 발생하는 지점은 아래 2가지이다.

1) 좌석 예약

2) 포인트 충전, 사용

 

좌석 예약 기능 동시성 제어 프로그래밍

1. synchornized - 부적합

Java에서 제공하는 가장 기본적인 동시성 제어 프로그래밍 방법이다.

좌석 예약 메서드에 synchronized를 걸면 싱글 스레드 방식으로 좌석 예약을 할 수 있다.

구현 난이도 - 쉬움

성능 - 낮음

특정 메서드를 단일 스레드 환경으로 걸어버리는 것이기 때문에 해당 메서드에 진입하기 위해서는 앞의 스레드가 작업이 끝날때까지 대기해야 한다. 성능과 효율성이 낮은 동시성 제어 방식이다. 그리고 어플리케이션 내의 메서드에 대해서 단일 스레드 환경으로 처리하기 떄문에

분산환경에서는 적합하지 않다는 단점이 있다. 서버가 여러대이면 synchronized가 의미가 없어진다.

 

2. ConcurrentHashMap - 부적합

synchronized 방식보다 개선된 방법이다. ConcurrentHashMap은 데이터별로 Lock을 개별적으로 걸기 때문에 메서드 전체에 잠금을 걸어버리는 synchronized보다 성능이 향상된다. 

구현 난이도 - 쉬움

성능 - 낮음

하지만 ConcurrentHashMap도 분산환경에서는 적합하지 않다. 여러대의 서버에서 콘서트 좌석 예약을 하는 경우 ConcurrentHashMap으로 동시성을 제어하는데 무리가 있다.

 

3. 낙관적 락 - 부적합

낙관적 락은 데이터의 version으로 수정여부를 확인하여 동시성을 제어하는 방식이다.

데이터 수정으로 충돌이 적고, 비즈니스 로직이 중간에 실패해도 문제가 없는 경우에 적합한 락의 방식이다.

그리고 요청의 대기시간도 비관적 락에 비해서 짧다. 그리고 여러개의 요청 중 1개만 성공시키고 나머지 탈락시켜야 하는 경우 사용하면 좋다.

구현 난이도 - 쉬움

성능 - 보통

위와 같은 장점이 있음에도 낙관적 락을 좌석 예약 시스템에서 채택하지 않은 이유는 몇 만명이 몰리는 경우 모든 요청 중 1건만 성공시키고 나머지는 모두 실패처리를 해야한다. 그리고 그에 따른 재시도 처리를 해야한다. 실패처리가 많을수록 재시도가 많아지고 서버에 부하가 증가하게 되기 때문에 낙관적 락을 채택하지 않았다.

낙관적 락으로 100개의 스레드로 좌석 예약을 시도했을 때 2.987초가 걸렸다.

 

 

4. 비관적 락 - 포인트 충전에 적합

사용자의 요청이 순차적으로 처리되어야 하는 경우 사용하면 좋다. 주로 금융시스템과 같이 반드시 처리가 되어야 하는 곳에서 많이 사용된다. 그리고 트랜잭션을 짧게 잡을 경우 처리 성능도 빠르게 가져갈 수 있다.

구현 난이도 - 쉬움

성능 - 좋음

특정 좌석에 대해서 비관적 락을 걸면 읽기와 쓰기가 잠긴다. 그래서 다른 스레드는 락이 해제될 때까지 대기해야 한다. 그렇기 때문에 정합성이 높다. 포인트 충전에 비관적 락이 적합한 이유는 충전 요청에 대해서 모두 순차적으로 처리해주기 때문이다. 하나의 요청도 유실되지 않고 요청한 만큼 포인트를 충전하기 때문에 정합성이 높다.

비관적 락의 경우 100개의 스레드로 좌석 예약을 시도하는 경우 3.3초가 소요된다. 낙관적 락보다는 오래 걸리는 것을 확인할 수 있다.

 

 

5. 분산 락 - 좌석 예약에 적합

분산 환경에서 Redis를 활용한 락이다. Pub/Sub 방식의 분산락은 락을 획득하면 해당 락으로 좌석 예약처리를 진행하고, 락을 획득하지 못하면 대기하다가 락이 해제되었다는 이벤트 리스너가 동작하면 그 때 락을 획득해서 처리하는 방식이다.

분산 락에는 스핀락, Pub/Sub, 심플락 등 여러가지가 있는데 Pub/Sub 방식이 효율적이다.

구현 난이도 - 쉬움

성능 - 좋음

분산락은 스레드 100개로 좌석 예약을 할 경우 3.189초가 소요되었다. 

 

속도 자체는 낙관적 락이 제일 빠르지만 재시도 처리 등을 고려했을 때 비관적 락은 포인트 충전, 분산 락은  좌석 예약에서 적합해보인다.

댓글