| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- 윤성우의 열혈 자료구조
- 메모리구조
- datastructure
- C programming
- Algorithm
- s
- 윤성우 열혈자료구조
- R
- coding test
- 이것이 자바다
- ㅅ
- Stack
- Selection Sorting
- insertion sort
- JSON
- Graph
- stream
- Serialization
- list 컬렉션
- 혼자 공부하는 C언어
- 알기쉬운 알고리즘
- buffer
- C 언어 코딩 도장
- 이스케이프 문자
- Today
- Total
Engineering Note
[SW Engineering] Redis 분산락을 통한 동시성 문제 해결 과정 본문
[SW Engineering] Redis 분산락을 통한 동시성 문제 해결 과정
Software Engineer Kim 2025. 12. 25. 15:16작성 예정
redis 동시성 제어 단계적 해결 과정과 해결과정중 발생한 원인 분석과 facade 패턴을 통한 최종 해결책
초기 코드의 문제는 프록시 객체를 통해 호출된 메서드가클래스 레벨의 @Transactional(readOnly = true) 트랜잭션에 참여하면서내부 메서드 호출 역시 해당 readOnly 트랜잭션의 영향을 받은 점이다.이로 인해 엔티티 변경과 더티 체킹은 발생했지만flush가 수행되지 않아 DB에는 UPDATE가 반영되지 않았다.이를 해결하기 위해 프록시 객체가 호출하는 메서드에@Transactional을 직접 선언하여 정상적인 쓰기 트랜잭션을 적용했으나,Redis 락 해제를 트랜잭션 커밋 이전에 수행하면서커밋되지 않은 상태에서 락이 해제되어결과적으로 Lost Update 문제가 발생했다.
그래서 최종 해결로 이거 맞아? facade 패턴으로 트랜잭션 외부에서 레디스락,해제를 적용해 해결
문제 해결과정 중 알게된 사실
Sping AOP, @Transactional 어노테이션 작동 방식, SQL auto commit, JPA 더티체킹, flush 적용 시점.
Redis 역할
Redis 분산락을 통해 DB 접근 가능 여부를 선제적으로 제어하여, 동시 요청 상황에서 DB의 트랜잭션 및 락 관리 부담을 효과적으로 완화하는 역할. 비관적락을 통해 발생하는 성능 저하이슈해결
효과
DB 커넥션 풀 부담도 줄여서 다른 요청을 처리할 수 있게 자원을 효율적으로 관리했다.
정리하면 분산락을 통해 동시성 제어를 DB 밖으로 이동시켜, DB 커넥션 풀과 트랜잭션 자원을 보호하고 전체 요청 처리량을 안정적으로 유지하는 구조로 설계했다.
알고 있어야 할 개념
1️⃣ @Transactional은 “어노테이션”이 아니라 “프록시 기반 경계”다
새로 정리된 포인트
- @Transactional은 메서드에 붙어 있다고 자동으로 동작하지 않는다
- Spring 프록시를 거쳐 호출될 때만 트랜잭션이 시작된다
- 같은 클래스 내부 메서드 호출(self-invocation)은 프록시를 타지 않는다
결과
- 내부 메서드에 @Transactional이 있어도 트랜잭션이 시작되지 않을 수 있다
- 트랜잭션은 “어디서 호출되느냐”가 핵심
📌 참고
Spring 공식 문서 – Declarative Transaction Management
https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative.html
2️⃣ “트랜잭션이 없다”는 말은 “SQL이 실행되지 않는다”는 뜻이 아니다
새로 정리된 포인트
- SQL은 트랜잭션 없이도 실행된다
- MySQL 기본 설정은 autocommit = ON
- 이 상태에서는 각 SQL이 개별 트랜잭션으로 실행된다
정확한 표현
- ❌ 트랜잭션이 없어서 SQL이 안 나간다
- ✅ 트랜잭션 경계 없이, SQL 하나당 즉시 커밋된다
📌 참고
MySQL 8.0 Reference – Autocommit
https://dev.mysql.com/doc/refman/8.0/en/commit.html
3️⃣ @Transactional의 진짜 역할은 “여러 SQL을 하나의 생명주기로 묶는 것”
새로 정리된 포인트
- @Transactional이 하는 일
- auto-commit을 끄고
- 여러 SQL을 하나의 논리적 작업 단위로 묶는다
- 예외 발생 시 전체 롤백
- DB 락을 트랜잭션 종료 시점까지 유지
반대로 없으면
- SQL1 → 즉시 커밋
- SQL2 → 즉시 커밋
- 중간 실패 시 되돌릴 방법 ❌
📌 참고
Spring Transaction Propagation
https://docs.spring.io/spring-framework/reference/data-access/transaction/propagation.html
4️⃣ DB 락은 “트랜잭션 생명주기”에 종속된다
새로 정리된 포인트
- DB 락(비관락, 낙관락)은 트랜잭션 안에서만 의미가 있다
- 트랜잭션이 끝나면:
- COMMIT → 락 해제
- ROLLBACK → 락 해제
그래서 생긴 깨달음
- 트랜잭션이 없으면
- 락이 걸려도 쿼리 종료와 동시에 해제
- 동시성 보호 효과 없음
📌 참고
MySQL InnoDB Locking
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
5️⃣ Redis 락과 DB 락은 “역할 레이어가 다르다”
새로 정리된 포인트
- Redis 락
- 애플리케이션 레벨
- “누가 먼저 들어오느냐”를 제어
- DB 락
- 데이터 레벨
- “데이터를 누가 수정 중이냐”를 제어
이번 코드에서의 실제 상태
- Redis 락 ✅ 정상
- DB 트랜잭션 ❌
- DB 락 ❌ 의미 없음
👉 진입은 막았지만, 데이터 보호는 못 한 구조
6️⃣ MySQL에서 트랜잭션을 명시적으로 시작하는 방법
새로 정리된 포인트
- 트랜잭션 시작 쿼리
- START TRANSACTION; BEGIN;
- 트랜잭션 종료
- COMMIT; ROLLBACK;
- BEGIN … END는 트랜잭션이 아니라 블록 문법
📌 참고
MySQL START TRANSACTION
https://dev.mysql.com/doc/refman/8.0/en/commit.html
7️⃣ 최종적으로 정리된 “핵심 문장 3개”
이 세 문장은 지금 단계에서 정확히 머리에 들어가 있으면 된다.
- @Transactional은 프록시를 거쳐야만 동작한다
- 트랜잭션이 없으면 SQL은 실행되지만 보호되지 않는다
- DB 락은 트랜잭션 안에서만 의미를 가진다
'SW Engineering' 카테고리의 다른 글
| JPA 일대다 List 컬렉션 사이즈 조회, 동작 방식 (1) | 2025.12.26 |
|---|---|
| [SW Engineering] Redis 분산락을 사용하는 이유와 기본적인 사용 방법 (0) | 2025.12.25 |
| [SW Engineering] 동시성 이슈가 발생하는 이유와 비관적 락을 통해 동시성 문제 해결하기 (0) | 2025.12.24 |
| [SW Engineering] JWT 기반 인증의 한계와 보안·제어성 개선 전략 (0) | 2025.12.20 |
| [SW Engineering] 이커머스 동시성 문제 해결 (0) | 2025.12.19 |
