-
동시성 문제를 해결하자프로젝트/미디어 스트리밍 서버 프로젝트 2022. 9. 30. 00:00반응형
개요
val findVideo = videoRepository.findByIdOrNull(hitId) ?: throw IllegalArgumentException() findVideo.updateHit() videoRepository.save(findVideo)
조회수를 증가시키기 위해 read and write 하는 작업을 수행하였습니다.
이때 트랜잭션 내 데드락 혹은 데이터 불일치 등의 발생 위험이 존재했습니다.
이를 해결하기 위한 방법은 어떤 것들이 있는지 그리고 어떤 방법을 적용하는 게 좋을지 찾아보는 시간을 가지려고 합니다.
문제가 발생하는 이유는?
위 상태는 3가지 작업으로 분리할 수 있습니다.
1. hit 변수의 값을 가져온다.
2. hit 변수의 값을 증가한다.
3. 변경된 hit 변수를 저장한다.
이때 스레드 1과 2가 동시에 작업을 수행할 경우 문제가 발생할 수 있습니다.
현재 hit = 1이라고 가정
스레드 1이 hit 변수의 값을 가져옵니다. (hit = 1)
스레드 1이 hit 변수의 값을 증가합니다. (hit = 2)
스레드 2가 hit 변수의 값을 가져옵니다. (hit = 1) *DB에는 아직 1 임
스레드 1이 hit 변수의 값을 저장합니다. *DB에 2로 저장
스레드 2가 hit 변수의 값을 증가합니다. (hit = 2)
스레드 2가 hit 변수의 값을 저장합니다. *DB에 2로 저장
3을 기대하였지만 우리는 2라는 결과를 마주하게 되는 문제가 발생합니다.
그림으로 도식화하면 다음과 같습니다.
https://velog.io/@been/자바Multi-Thread환경에서-동시성-제어를-하는-방법 애플리케이션단에서 동시성을 제어하는 방법으로는 다음과 같은 방법들이 존재합니다.
AtomicInteger(CAS 알고리즘), volatile, synchronization
- CAS 알고리즘 : 현재 스레드에 저장된 값과 메인 메모리에 저장된 값을 비교 → 일치 시 새로운 값으로 교체, 불일치 시 실패하고 재시도
1. Isolation Level 변경하기
예를 들어 트랜잭션의 격리 수준을 SERIALIZABLE로 설정되면 읽기 작업도 공유 잠금(읽기 잠금)을 획득해야 하며, 동시에 다른 트랜잭션은 그러한 레코드를 변경할 수 없습니다.
즉, 한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서는 절대 접근할 수 없습니다.
2. Lock 사용하기(SELECT ... FOR UPDATE)
SELECT gold FROM players WHERE id = 1 FOR UPDATE;
FOR UPDATE를 SELECT를 가져온 이후로 해당 ROW에 대해 다른 세션의 SELECT, UPDATE, DELETE 등의 쿼리가 모두 잠김 상태가 됩니다.
즉, FOR UPDATE를 한 세션 외에 다른 세션들은 모두 해당 ROW에 접근을 할 수 없게 되고, 모두 대기 상태가 된다.
트랜잭션이 끝나는 시점에서 풀립니다.
3. JPA versioning 사용하기 (낙관적 락)
https://sabarada.tistory.com/175 도식도를 글로 나타내면 아래와 같습니다.
- A가 table의 Id 2번을 읽음 ( name = Karol, version = 1 )
- B가 table의 Id 2번을 읽음 ( name = Karol, version = 1 )
- B가 table의 Id 2번, version 1인 row의 값 갱신 ( name = Karol2, version = 2 ) 성공
- A가 table의 Id 2번, version 1인 row의 값 갱신 ( name = Karol1, version = 2 ) 실패
- Id 2번은 이미 version이 2로 업데이트되었기 때문에 A는 해당 row를 갱신하지 못함
낙관적 락은 CAS알고리즘과 같다는 생각이 듭니다.
4. DB atomic operation
update [테이블이름] set count = count + 1 where id=[pk id]
update만 하게 돼도 동시성 보장이 된다고?라는 생각이 들었습니다.
단일 UPDATE/DELETE문은 원자적이어서 작업이 진행 중인 동안 다른 보류 중인 트랜잭션은 행을 수행할 수 없습니다.
다음은 stackoverflow에 대한 링크입니다.
https://stackoverflow.com/questions/4358732/is-incrementing-a-field-in-mysql-atomic
그래서 뭘 써야 할까?
2번(비관적 락) vs 3번(낙관적 락)을 비교했을 때는 낙관적 락은 확실히 락을 실제로 걸지 않는 만큼 성능 쪽에서 유리할 수 있습니다.
하지만 외부 API를 호출하는 경우가 로직에 포함되어 rollback이 일어나는 경우에는 외부 API는 rollback이 일어나지 않을 수 있기 때문에 비관적 락을 통해 API가 호출되지 않는 방향으로 선택해야 합니다.
2번(비관적 락) vs 4번(DB atomic operation)을 비교했을 때는 사실 로직상으로 lock을 건다고 생각해서 동일하다고 생각합니다.
하지만 복잡한 비즈니스 로직이 첨부된다면 DB atomic operation에는 쓰기가 힘들어진다고 생각이 듭니다.
따라서 복잡한 비즈니스로직이 존재하면 select... for update로 값을 읽어오고 비즈니스 로직을 수행하고 update 하는 방식을 사용하면 될 것 같습니다.
하지만 간단한 작업은 4번으로 해결하면 좋을 것 같습니다.
1번(isolation level) vs 4번(DB atomic operation)을 비교했을 때는 Serializable을 고립 레벨로 설정하면 동시성이 너무 떨어질 거라 생각이 들었기 때문에 4번으로 해결하면 좋을 것 같습니다.
결론
현재 조회수를 증가시키는 간단한 로직을 사용하고 있으니 4번(DB atomic operation)을 통해 해결하면 좋을 것 같습니다.
더 나아가서 여러 개의 서버가 락을 공유하는 분산 락에 대해서도 알아보고 싶으신 분들은 다음 글을 읽어보시면 좋을 것 같습니다.
https://hyperconnect.github.io/2019/11/15/redis-distributed-lock-1.html
레디스와 분산 락(1/2) - 레디스를 활용한 분산 락과 안전하고 빠른 락의 구현
레디스를 활용한 분산 락에 대해 알아봅니다. 그리고 성능을 높이고 일관성을 보장하는 방법에 대해 알아봅니다.
hyperconnect.github.io
출처
https://mygumi.tistory.com/111
Atomic Operation이란? :: 마이구미
이번 글은 Atomic Operation 에 대해 다뤄본다. Atomic Operation은 무엇인가? Atomic Operation의 개념은 재진입성, 임계 구역, 스레드 안전, 동기화 프리미티브 등 관련 용어를 이해하는 데 많은 도움을 준다.
mygumi.tistory.com
https://dev-monkey-dugi.tistory.com/151
Redis 동시성 이슈 개선하기
Redis에서 조회수 증가 동시성 이슈 조회수를 Redis에 캐싱하고 나중에 RDB로 write back해야하는 일이 있었다. Redis에서 해야하는 일은 게시물이 조회되면 → 해당 게시물의 id를 key로하여 조회수를 저
dev-monkey-dugi.tistory.com
https://sabarada.tistory.com/175
[database] 낙관적 락(Optimistic Lock)과 비관적 락(Pessimistic Lock)
안녕하세요. 오늘은 낙관적 락과 비관적 락의 개념에 대해서 알아보는 시간을 가져보도록 하겠습니다. DB 충돌 상황을 개선할 수 있는 방법 database에 접근해서 데이터를 수정할 때 동시에 수정이
sabarada.tistory.com
https://velog.io/@been/자바Multi-Thread환경에서-동시성-제어를-하는-방법
[자바]Multi Thread환경에서 동시성 제어를 하는 방법
프로세스특정 작업을 수행하는 소프트웨어를 프로그램이라고 한다.프로그램이 실제로 실행되어, 메모리나 CPU와 같은 자원을 할당받으면 이를 프로세스라고 부름스레드는 이 프로세스를 구성
velog.io
MySQL에서 동시성 문제를 해결하기 위한 Lock
InnoDB에서 잠금은 두가지로 나눌 수 있다. FOR SHARE (LOCK IN SHARE MODE) FOR UPDATE FOR SHARE (LOCK IN SHARE MODE) 트랜잭션이 끝날 때까지 SELECT를 한 Row 값이 변경되지 않는 것을 보장한다. 해당 Row를..
skasha.tistory.com
https://forums.mysql.com/read.php?25,401854,401992#msg-401992
MySQL :: MySQL Forums
Forums Topics Posts Last Post Announcements MySQL related Product and Service announcements. 1,203 1,294 July 26, 2022 10:10PM Topics Posts Last Post Newbie Forum for New Users of MySQL. 63,935 192,973 August 23, 2022 04:05PM Install & Repo Forum for Insta
forums.mysql.com
'프로젝트 > 미디어 스트리밍 서버 프로젝트' 카테고리의 다른 글
@Transactional 롤백과 @TransactionalEventListener (0) 2022.10.04 @Embedded vs @OneToOne (0) 2022.10.03 배포 스크립트 작성하기 (0) 2022.09.29 kotlinDSL + RestDocs 적용하기 (0) 2022.09.28 MultipartFile 컨트롤러 단위 테스트(MockMvc) (0) 2022.08.24