단일서버에서는 synchronized
다중서버에서는 DB X-Lock
하지만 DB를 Lock하면 단점이 있다.
"레코드(행) 자체를 Lock"을 한다는 큰 단점이 존재한다.
만약 X-Lock이 걸린 `좋아요👍`를 50000명이 동시에 눌렀다.
많은 요청을 처리하는 도중, `좋아요👍` 개수를 조회한다면?
X-Lock은 쓰기는 물론 읽기도 제한되어 있는 상태라 조회할 수 없다.
그래서 다른 동시성 해결책을 말해볼려한다.
Redis
{ Key, Value } 구조의 비정형 데이터를 저장하고 관리하기 위한 NoSQL.
캐시(Cache), 메세지 브로커(MQ)로 사용되며, 인메모리 데이터 구조를 가진 저장소.
(레디스가 뭔지는 요기)
두괄식으로 말하자면, Redis를 사용하는 분산락(Distributed-Lock)으로 해결하면 된다.
일단 DB Lock도 문제점이 한두개가 아니다...
Redis의 큰 장점은 "인메모리(In-memory)" DB로, RAM에 데이터가 저장된다.
기존의 DB Lock은 디스크(HDD, SSD)에 있는 데이터를 Locking 한다.
DataBase 주체로 Lock을 거는 DB-Lock에 비해,
메모리(RAM)을 사용하는 Redis를 이용한 Lock이 성능이 훨씬 좋다.
위에서도 말했지만, 테이블의 레코드 자체를 Lock을 하게 되면,
다른 스레드에서 그 테이블의 레코드를 참조할 수 없다.
즉, 쌓인 좋아요👍 증가 요청을 처리하는 동안, 좋아요👍 개수를 조회할 수 없다는 큰 단점이 있다.
지금 난 단순하게 좋아요👍만 Locking 하는 상태..
근데 RDB 특성 상, 테이블끼리 관계로 얽혀있다.
한개의 메소드에서 여러개의 테이블 레코드를 사용하지 말라는 법이 있는가?
여러 테이블의 레코드를 참조하는 요청의 비관적 락 사용 시, 데드락이 발생할 확률이 높다.
DB를 한 대가 아닌, 트래픽 분산을 위해 여러 대의 서버로 구성한 환경
SELECT * FROM post WHERE id = ? FOR UPDATE;
DB 서버가 3대가 있다고 가정하자.
이 Query를 사용하면, DB1 테이블의 레코드에 베타락(X-Lock)이 걸린다.
그럼 나머지 DB2, DB3는 Lock이 걸린 사실도 모른다.
synchronized랑 비슷한 원리다.
DB-Lock은 다중 WS 서버는 해결이 가능하지만,
스케일 아웃된 DB 환경에서는 동시성을 제어할 수 없다.
분산락 (Distributed-Lock)
여러 서버가(프로세스) 공유 데이터를 제어하기 위한 기술

Java에서 사용하는 Redis 클라이언트 라이브러리는 총 3개가 있다.
Java에서 Redis를 사용하기 위해 만든 클라이언트 라이브러리
https://github.com/redis/jedis
동기 방식의 클라이언트다.
초창기 클라이언트다 보니, 비동기 프로그래밍이 지금처럼 중요하지 않은 것 같다.
초기 클라이언트다 보니 구조가 단순하고, 설정이 거의 필요 없었다.
Redis 명령어를 거의 1:1로 대응하는 API를 지원해서, 배우기 쉽고 직관적이다.
단 동기 방식으로 작동하여, Blocking 이슈가 발생 가능하다는 단점이 있다.
Jedis의 단점을 보완한 오픈소스 클라이언트 라이브러리.
https://github.com/redis/lettuce
동기, 비동기 방식을 둘 다 지원한다.
그래서 non-blocking하게 요청을 처리할 수 있고, 확장성이 뛰어나다는 장점이 있다.
하지만 기능이 많으면 많을수록... 사용하는 방식이 어렵다는 단점이 있다.
Spring Boot는 2.x 버전 이상부터 Lettue만 지원한다.
implementation('org.springframework.boot:spring-boot-starter-data-redis')
우리가 기본적으로 추가했던 의존성은 Lettuce 라이브러리다.
이 라이브러리는 Setnx 명령어 & 스핀락(spin-lock) 방식으로 락을 구현할 수 있다.
스핀락 (spin-lock)
락을 획득할 때까지, 계속해서 반복적으로 CPU를 지속적으로 사용하면서 시도하는 방식.
SETNX (Set If Not Exist)
특정 key 값이 존재하지 않을 경우에 set 하라는 명령어.
(즉, 이미 key가 있으면 False, 없으면 True)
순서는 이렇게 된다.
Redis를 활용한 고급 자바 클라이언트. Redlock 알고리즘을 공식적으로 지원
Redisson은 Redis의 Pub/Sub 기능을 통해 Lock을 제어한다.
예시로, A가 좋아요👍를 누가 눌렀다.
좋아요👍는 Publisher(Pub)가 되고, lock 이름 기반 key가 채널ID 역할이 되어 Subscriber(Sub)하게 된다.
(예: lock:like:post:1)
좋아요👍 개수를 1로 증가시킨 후, DB에 Commit이 완료가 되는 순간!
채널 Sub을 해제하게 된다.
근데 만약... 구독 ~ Commit 사이에 B가 좋아요👍를 누르면?
채널에서 대기하고 있다가, A가 구독을 해제하는걸 구독자에게 전파하는 순간에 B의 연산이 시작된다.
다음번엔
를 해볼 생각이다.