inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

묻고 답해요

176만명의 커뮤니티!! 함께 토론해봐요.

Redis 활용하기 문제점 해결하기 부분이 이해가 잘 안됩니다...

미해결

실습으로 배우는 선착순 이벤트 시스템

위 도표를 보여주시면서 설명을 해주실때, 10시 정각에 스레드 1이 쿠폰 발급 로직을 시작 10시 2분에 완료가 된다면, 스레드 2는 스레드 1의 작업이 모두 종료될때까지 기다렸다림. 스레드 2는 10시 2분에 작업을 시작 이렇게 설명해주셨는데, 이게 잘 이해가 안됩니다... 제 생각에는 아래와 같이 10시에 동시에 처리되는 흐름이 되어야 할 것 같은데요... 즉, 스레드2는 쿠폰 생성 로직이 끝날 때까지 기다리지 않고, 스레드 1이 쿠폰 발급 로직을 수행 중이라도 스레드 2는 곧바로 Redis 값을 받고 독립적으로 로직을 수행 할 수 있을 것 같은데 아닌가요?? 레디스 자체는 싱슬스레드이니 레디스를 찌를때는 무조건 하나씩만 들어가겠지만 스레드 1은 레디스에 찌르고 값을 받아온 후 본인의 비즈니스 로직 수행할때 스레드2가 레디스 서버에 찌를 수는 없는건가요? 제가 이렇게 이해한 부분이 잘못된 것인지, 아니면 제가 강의 설명을 오해한 것인지 혼란스러워 질문을 드리게 되었습니다.

  • java
  • docker
  • spring-boot
  • kafka
  • redis
차니 댓글 1 좋아요 0 조회수 271

DDL문 정리되어 있는 파일 있을까요?

해결됨

은행 서버 프로젝트 실습을 통해 배우는 코틀린 마스터 클래스

DDL문 정리되어 있는 파일 있을까요? 복붙하면 편할 것 같아서요

  • kotlin
  • mysql
  • mongodb
  • kafka
  • redis
gusang 댓글 2 좋아요 0 조회수 262

java, kotlin

미해결

[아파치 카프카 애플리케이션 프로그래밍] 개념부터 컨슈머, 프로듀서, 커넥트, 스트림즈까지!

안녕하세요 좋은 강의 감사합니다. 말씀주신 카프카의 중요 개념적인 것들 (컨슈머, 프로듀서 등등) 이 자바에 국한된 것이라고 하셨었잖아요. 예를 들어 js 같은 것들은 third party 로 구분되기에 카프카의 모든 것들이 포함안될 수도 있다구요~! 그렇다면 당연히 kotlin 도 java 랑 동일하다고 보면 되겠죠? 감사합니다.

  • kafka
  • 데이터-엔지니어링
highjune 댓글 2 좋아요 0 조회수 189

강의 인덱스 접근 관련 문의드립니다.

해결됨

스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판

안녕하세요! 강사님 늘 강의 잘 듣고 있습니다. 다름이 아니고 아래 사항에 대해 궁금한 부분이 있는데요. (아래 강의 20:16 시점입니다) https://inf.run/1668e secondary index -> clustered index로 limit 개수만큼 접근해주신다고 설명해주셨는데요 ! where board_id = 1만 조건이 있어서 이미 어떤 row인지 세컨더리 인덱스에서 판단이 가능할 것 같은데, 클러스터 인덱스로 접근하게 되는 이유가 어떻게 되는걸까요? 만약 where board_id = 1 AND writer_id = 1 이 있다면 writer도 검증이 필요하기 때문에, 클러스터 인덱스 까지 가는 부분은 이해되기는 합니다만 board_id만 조건이 걸린 상태에서 클러스터 인덱스도 간다는 부분을 보고 이해가 잘 안되어서 문의드립니다 .. !

  • java
  • mysql
  • spring-boot
  • kafka
  • redis
자기개발하고싶어요 댓글 2 좋아요 0 조회수 211

24시간 이내의 최신글 API 설계 관련 질문

해결됨

스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판

쿠케님 안녕하세요! 그간 안녕하셨는지요. 몇 주 놀다가 다시 반성하고 각성 모드로 강의 시청 하고 있습니다. Redis에 24시간 이내의 최신글을 저장하는거 까지는 이해했습니다! 그렇다면 레디스에 저장한 최신글은 프론트에서 어떻게 활용할수 있는 걸까요? 제가 생각한 아래 흐름이 맞는지 검토 부탁드립니다! 서버 게시판 종류마다 24시간 이내의 최신글을 레디스에 저장 게시판 종류와 데이터 개수를 request로 받는 최신글 API를 별도로 만듬 프론트 사이트의 메인 페이지 일부 화면에 특정 게시판의 최신글을 뿌려주는 요구사항이 존재 서버의 최신글 API를 호출하여 각 게시판 종류마다 최신글을 뿌려줌 감사합니다~!

  • java
  • mysql
  • spring-boot
  • kafka
  • redis
soap 댓글 2 좋아요 0 조회수 166

테이블 설계관련 문의

해결됨

스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판

안녕하세요. 예전부터 목록 조회 관련 궁금한게 있었는데 어떻게 설계하는게 좋은지 몰라서 문의드립니다. 강의의 예제처럼 하나의 테이블에 대해서 게시글 목록을 보여주는 기능들은 어떻게 해야하는지 잘 이해가 됩니다. 근데 보통 개발을 하다 보면 리스트를 보여주는 경우가 많잖아요. 그런 ux들을 보면 3~4개 이상의 테이블을 join하고, 여러 테이블에 대한 필터링도 하고, 여러 테이블의 여러 컬럼에 대해서도 정렬까지 하고, 페이지네이션까지 해야 하는 경우가 많았던 것 같습니다. 거기에 N:M구조를 join하는 경우가 있다면... 이런 기능들을 구현하려면 아무리 머리를 굴려도 full scan이 발생할 수 밖에 없을 거 같은데요. 필터링이나 정렬이 각각 다른 테이블 기준으로 선택이 된다면 속도가 나올 수 없어 보입니다. 대책을 생각해보면 그냥 원래 느리니까 db 성능을 믿고 그냥 join해서 보여줄수도 있고, 아니면 전체를 join 한 새로운 테이블을 만들기도 하는건지 궁금하네요. 근데 만약 새로운 테이블을 만들었더라도 N:M구조가 있었다면 또 1:N 구조가 여전히 남아있을테니 또 성능이 좋지 않을것이기에 생각이 많아지네요.

  • java
  • mysql
  • spring-boot
  • kafka
  • redis
굿데이 댓글 2 좋아요 0 조회수 160

@KafkaListener에 대해서

해결됨

스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판

안녕하세요 강의 잘보고 있습니다. 알아보니까 @KafkaListener가 비동기로 실행되는 줄 알았는데 동기로 실행이 되네요 강의를 보면서 느낀점이 카프카로부터 구독할 때 비동기로 실행되어야 하지 않나싶은데 어떤지 알려주시면 감사합니다

  • java
  • mysql
  • spring-boot
  • kafka
  • redis
DongA Shin 댓글 2 좋아요 0 조회수 115

와 이리 게시판이 조용하노

해결됨

은행 서버 프로젝트 실습을 통해 배우는 코틀린 마스터 클래스

이 강의 느낌 왔습니다. 찍먹이 아닌 부먹하겠습니다. 평일 저녁, 주말이 심심하지 않겠다 ㅎㅎ(벚꽃 어디갔노) 강의 찍어주셔서 감사합니다.

  • kotlin
  • mysql
  • mongodb
  • kafka
  • redis
soap 댓글 1 좋아요 0 조회수 226

Transactional Outbox 모듈 적용에서 빈이 실습과 다릅니다

해결됨

스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판

안녕하세요 다름이 아니라 application.yaml에서 설정값 모두 맞추고, gralde 빌드도 되었습니다 그런데 ArticleApplication 실행하는 부분부터 강의와 다르게 정상실행이 되고, ArticleService에서 OutboxEventPublisher 빈을 못 찾고 있습니다 outbox-message-relay 패키지 내 설정값 혹은 어노테이션 살펴봐도 감이 잘 오지 않습니다 지식공유자님의 코드와 샅샅이 차이점을 찾으려 했으나, 차이는 메세지릴레이 부분 뿐이었습니다 위 빈 문제를 해결하기 위해서 어느 부분을 확인하면 될 지 여쭤보려 합니다

  • java
  • mysql
  • spring-boot
  • kafka
  • redis
woo 댓글 2 좋아요 0 조회수 180

MSA 개발 배포 방식에 대한 선택과 방향성

미해결

Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)

안녕하세요! 강의를 마친 후, 여러 가지 궁금한 점이 생겼습니다. 지금까지 로컬,및 도커에서 사용한 config-service, apigateway-service, discovery-service(유레카 서버) 의 역할이 Kubernetes 환경에서 대체될 수 있다는 것을 배웠습니다. 구체적으로는: 유레카 대체: 각 Pod의 DNS 이름을 사용하여 서비스 간 통신 API Gateway 대체: Ingress 리소스를 사용하여 외부 요청을 라우팅(강의엔 없음) Config 서버 대체: RabbitMQ를 통한 busrefresh로 설정 정보를 업데이트하는 대신, Kubernetes의 ConfigMap을 사용하여 설정을 동적으로 업데이트 이렇게 Kubernetes에서 많은 기능을 간편하게 처리할 수 있다는 점에 대해 배우면서, 이전의 복잡한 서비스 구조가 생각보다 간단하게 대체될 수 있다는 점에서 약간 허무한 느낌도 듭니다. 강의를 마친 후의 궁금증은, 실제 MSA 개발 시에 Kubernetes, Docker, AWS를 사용하는 다양한 배포 방식 중 어떤 선택을 해야 할지에 대한 부분입니다. EC2에 Docker 사용하여 배포 EC2에 Kubernetes 사용하여 배포 AWS의 EKS, ECS, API Gateway를 사용하는 방법 등 여러가지 방법이 있을텐데요 이와 같은 다양한 옵션 중에서 어떤 방식을 선택 해야하는지 공부의 방향성을 제시해주시면 감사하겠습니다. 좋은 강의 감사합니다!

  • spring-boot
  • jpa
  • 아키텍처
  • spring-cloud
  • kafka
  • msa
  • rabbitmq
kkkkkk 댓글 2 좋아요 0 조회수 287

마지막강의 k8s catalog 404 오류

미해결

Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)

serivce 오타네여

  • spring-boot
  • jpa
  • 아키텍처
  • spring-cloud
  • kafka
  • msa
  • rabbitmq
댓글 2 좋아요 0 조회수 138

기기 관련 질문입니다!

해결됨

은행 서버 프로젝트 실습을 통해 배우는 코틀린 마스터 클래스

mac 어떤 스펙의 기기를 쓰고 계신지 궁금합니다!

  • kotlin
  • mysql
  • mongodb
  • kafka
  • redis
hash04 댓글 2 좋아요 0 조회수 260

섹션4 -> 5 넘어가는시점 강의 영상이 중간에 빠진건아니신지..?

해결됨

커머스 서비스로 배우는 NestJS 실전 개발 (w. Prisma, Docker, Redis, Kafka)

섹션 5에 불필요한 코드정리에 대해서 영상도입부에 했다고하셨는데 그부분에 대한 영상은 빠져있는거같습니다

  • docker
  • kafka
  • redis
  • nestjs
  • prisma
벼룩 댓글 2 좋아요 2 조회수 291

java.nio.BufferUnderflowException 에러 발생합니다..

미해결

카프카 완벽 가이드 - 코어편

안녕하세요 해당 강의듣고 있는데 Java 기반에서 Producer 구현하기02를 보고 카프카 브로커에 메시지를 보냈습니다. 환경은 다음과 같습니다. 카프카 브로커: ec2 환경 (포트 열린건 확인했슴니당..) 카프카 컨플루언트 7.1.2 프로듀서: 로컬환경(맥) 카프카클라이언트 3.1.0 프로듀서(로컬)에서 메시지 send를 하면 java.nio.BufferUnderflowException해당 에러만 발생합니다. [kafka-producer-network-thread | producer-1] ERROR org.apache.kafka.clients.producer.internals.Sender - [Producer clientId=producer-1] Uncaught error in kafka producer I/O thread: java.lang.IllegalStateException: There are no in-flight requests for node 1 at org.apache.kafka.clients.InFlightRequests.requestQueue(InFlightRequests.java:62) at org.apache.kafka.clients.InFlightRequests.completeNext(InFlightRequests.java:70) at org.apache.kafka.clients.NetworkClient.handleCompletedReceives(NetworkClient.java:872) at org.apache.kafka.clients.NetworkClient.poll(NetworkClient.java:569) at org.apache.kafka.clients.producer.internals.Sender.runOnce(Sender.java:328) at org.apache.kafka.clients.producer.internals.Sender.run (Sender.java:243) at java.base/ java.lang.Thread.run (Thread.java:833) [kafka-producer-network-thread | producer-1] INFO org.apache.kafka.clients.NetworkClient - [Producer clientId=producer-1] Node 1 disconnected. 해당 에러가 반복되고있고. 해결방법을 못찾겠네요 ㅠㅠ 트러블슈팅해봐도 감을 못잡겠어서 도움요청드려요.. 카프카 브로커를 도커로 띄워놓고 logs로 보는데, 아무런 로그가 생성되지않고 있습니다! 제대로 send되는 것 같지 않아서 뭘 봐야되는지 모르겠네요..

  • kafka
  • 데이터-엔지니어링
임예준 댓글 3 좋아요 0 조회수 229

비관적 락 1에 대해서 질문입니다.

해결됨

스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판

안녕하세요 비관적 락에 대해서 잘 배우고 있습니다. 비관적 락1이 단순히 네이티브 쿼리를 이용하여 update 문 해서 바로 count를 수정하는건데 초반에 알려주신 조회할 때, 조회 락이 안 걸려서 데이터 일관성이 깨지는 거 알려주셨는데 비관적 락 1은 조회 자체를 생각하고 바로 갱신만해서 좋아요 수 카운트를 하는 건가요? 만약에 그렇다면 업데이트 시 락이 걸려 다른 트랜잭션 접근이 안되어 일관성이 유지가 되는 것은 이해가 갑니다. 테스트시 100번 동시 접근할 때 비관적 락2처럼 정합성이 보장되는거면 굳이 1이 더 빠르고 정확할텐데 비관적 락2를 쓰는 이유가 궁금합니다. 그리고 챗gpt한테 물어보니 단순히 update문하여 수정하는 것은 동시성 접근에 해결책이 안된다고 비관적 락 2쓰라고 하는데 테스트에서는 동시 100개가 접근해도 일관성이 충족이 되는거 보니 어느쪽이 맞는건지 모르겠습니다

  • java
  • mysql
  • spring-boot
  • kafka
  • redis
DongA Shin 댓글 2 좋아요 1 조회수 263

grafana 그래프 nodata

미해결

Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)

https://www.inflearn.com/community/questions/267420/%EB%8C%80%EC%89%AC%EB%B3%B4%EB%93%9C%EC%97%90-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EB%AA%BB%EA%B0%80%EC%A0%B8%EC%98%A4%EB%8A%94%EA%B2%83-%EA%B0%99%EC%8A%B5%EB%8B%88%EB%8B%A4 를 참고하여 Legend: {{instance}} Instant 체크 + Fields 입력 을 통해 위에 처리량은 잘나오는 모습입니다 하지만 밑에 그래프들은 Fields 입력하는 부분이없어서 처리량때 처럼 설정을 할 수 없고 역시나 no data로 나오네요 설정은 이대로 했습니다.

  • spring-boot
  • jpa
  • 아키텍처
  • spring-cloud
  • kafka
  • msa
  • rabbitmq
kkkkkk 댓글 2 좋아요 0 조회수 275

강의안 문제

미해결

Kafka & Spark 활용한 Realtime Datalake

강의안이 다 안 올라와 있는 것 같습니다

  • kafka
  • apache-spark
  • pyspark
  • data-lake
박민재 댓글 3 좋아요 0 조회수 208

각 사용자는 게시글 1개당 1번 조회수 증가'일 경우 설계 질문 드립니다.

해결됨

스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판

안녕하세요 쿠케님! 강의에 나온 10분의 TTL이 없을시 조회수 증가 관련되서 질문드립니다!' 현 상황에서 게시글 접근시 마다 조회수 증가 API를 호출하고 있습니다. 테이블 정보는 다음과 같습니다. Board 테이블 id(auto-increment) readCount BoardRead 테이블 id(auto-increment) boardId userId 게시글 상세 접근시 마다 호출하는 '조회수 증가 API'의 흐름은 다음과 같습니다. 1. 해당 게시글을 유저가 조회 했는지 검증 (BoardRead 테이블에서 검증) 1-1. 조회 한 이력이 있으면 return; 2. Board 비관적 락 조회 3. Board 테이블 readCount 업데이트 4. BoardRead 테이블 insert Board의 readCount는 게시글 조회수를 나타내고, BoardRead는 '각 사용자는 게시글 1개당 1번 조회수 증가'를 검증하기 위한 용도 입니다. '각 사용자는 게시글 1개당 1번 조회수 증가' 정책을 반드시 가져가야 한다면 BoardRead 테이블에 있는 데이터도 레디스로 옮겨야 할까요? 그런데 조회수 데이터는 계속해서 쌓일테고 비즈니스에 중요하지 않은 데이터가 레디스 메모리만 차지하는 느낌이 들어서 꺼려지더 라구욤.. 강의 내용대로 TTL을 걸수 밖에 없는건가 고민도 듭니당.. (레디스는 클러스터 환경으로 사용하고 있습니다.) 기능은 그대로 유지하되 비관적 락을 뺄 수 있는 방법이 있을까요? ※ 번외로 트래픽 바로 몰리니까 비관적 락 로직 때문인지 잠금 이슈 나서 디비 바로 터졌버렸네요 하하하 ㅠ 비관적 락을 선호하지 않는 이유를 체감해버렸다.. ※ 뇌 + GPT 갈구니까 아래와 같은 여러 결론이 나왔습니다. 해결책1 1. Redis SET 자료구조로 중복 체크, 최초 조회면 TTL 걸어줌 2. 분산락(Redis)을 걸고, Board 테이블 readCount 업데이트 및 BoardRead Insert 수행 3. 락 해제 해결책2 1. Redis SET 자료구조로 중복 체크, 최초 조회면 TTL 걸어줌 2. 비동기로 Board 테이블 readCount 업데이트(낙관적 락 적용) 및 BoardRead Insert 수행 작성하면서 문득 'readCount를 정규화 할까?' 했는데 스케이링 넘 클것 같네요.. 이유는 테이블 설계를 JPA의 상속을 활용하는 방안으로 했기 때문에 readCount 필드가 '게시판'이란 추상 클래스에 위치해 있습니다. 주저리 주저리 적어봤는데 머릿속에 혼란이 오네요 ㅠㅠ 자기전 마지막 생각 레디스의 incr를 이용해 조회수 관리, 조회수 데이터는 mysql에 주기적으로 백업

  • java
  • mysql
  • spring-boot
  • kafka
  • redis
soap 댓글 2 좋아요 0 조회수 296

route 설정이 동작을 안하는거 같아요.

미해결

Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)

RouteLocator gatewayRoutes(RouteLocatorBuilder builder) 이 메소드를 만들기 위해 implementation 'org.springframework.cloud:spring-cloud-starter-gateway' 이걸로 수정했을 했는데 이후 저 메소드를 주석 처리하고 applicatio.yml을로 설정을 대체 했을 때는 동작을 안하고 implementation 'org.springframework.cloud:spring-cloud-starter-gateway-mvc' mvc로 다시 변경해줘야 올바르게 동작하는데 무슨 차이가 있을까요?

  • spring-boot
  • jpa
  • 아키텍처
  • spring-cloud
  • kafka
  • msa
  • rabbitmq
ybs1121 댓글 2 좋아요 0 조회수 172

댓글 수 구현에서 동시성 문제 해결 질문드립니다

해결됨

스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판

학습 관련 질문을 최대한 상세히 남겨주세요! 고민 과정도 같이 나열해주셔도 좋습니다. 먼저 유사한 질문이 있었는지 검색해보세요. 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 안녕하세요 댓글 수 구현 강의를 해보다가 동시성 문제를 해결해보고 싶어서 비관적 락 for update를 사용하는 방법으로 한번 코드를 짜보고 테스트를 해보고 있습니다. 코드는 아래처럼 짜보았습니다 @Table(name = "article_comment_count") @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @ToString public class ArticleCommentCount { @Id private Long articleId; private Long commentCount; public static ArticleCommentCount init(Long articleId, Long commentCount) { ArticleCommentCount articleCommentCount = new ArticleCommentCount(); articleCommentCount.articleId = articleId; articleCommentCount.commentCount = commentCount; return articleCommentCount; } public void increase() { this.commentCount++; } public void decrease() { this.commentCount--; } } public interface ArticleCommentCountRepository extends JpaRepository<ArticleCommentCount, Long> { @Lock(LockModeType.PESSIMISTIC_WRITE) Optional<ArticleCommentCount> findLockedByArticleId(Long articleId); } @Service @RequiredArgsConstructor public class CommentService { private final CommentRepository commentRepository; private final Snowflake snowflake = new Snowflake(); private final ArticleCommentCountRepository articleCommentCountRepository; @Transactional public CommentResponse create(CommentCreateRequest request) { Comment parent = findParent(request); Comment comment = commentRepository.save( Comment.create( snowflake.nextId(), request.getContent(), parent == null ? null : parent.getCommentId(), request.getArticleId(), request.getWriterId() ) ); ArticleCommentCount articleCommentCount = articleCommentCountRepository.findLockedByArticleId(request.getArticleId()) .orElseGet(() -> { ArticleCommentCount newCount = ArticleCommentCount.init(request.getArticleId(), 0L); articleCommentCountRepository.save(newCount); return newCount; }); articleCommentCount.increase(); articleCommentCountRepository.save(articleCommentCount); return CommentResponse.from(comment); } private Comment findParent(CommentCreateRequest request) { Long parentCommentId = request.getParentCommentId(); if (parentCommentId == null) { return null; } return commentRepository.findById(parentCommentId) .filter(not(Comment::getDeleted)) .filter(Comment::isRoot) .orElseThrow(); } public CommentResponse read(Long commentId) { return CommentResponse.from(commentRepository.findById(commentId).orElseThrow()); } @Transactional public void delete(Long commentId) { commentRepository.findById(commentId) .filter(not(Comment::getDeleted)) .ifPresent(comment -> { if (hasChildren(comment)) { comment.delete(); } else { delete(comment); } }); } private boolean hasChildren(Comment comment) { return commentRepository.countBy(comment.getArticleId(), comment.getCommentId(), 2L) == 2; } private void delete(Comment comment) { commentRepository.delete(comment); articleCommentCountRepository.findLockedByArticleId(comment.getArticleId()) .ifPresent(articleCommentCount -> { articleCommentCount.decrease(); articleCommentCountRepository.save(articleCommentCount); }); if(!comment.isRoot()) { commentRepository.findById(comment.getParentCommentId()) .filter(Comment::getDeleted) .filter(not(this::hasChildren)) .ifPresent(this::delete); } } public CommentPageResponse readAll(Long articleId, Long page, Long pageSize) { return CommentPageResponse.of( commentRepository.findAll(articleId, (page - 1) * pageSize, pageSize).stream() .map(CommentResponse::from) .toList(), commentRepository.count(articleId, PageLimitCalculator.calculatePageLimit(page, pageSize, 10L)) ); } // 무한 스크롤 public List<CommentResponse> readAll(Long articleId, Long lastParentCommentId, Long lastCommentId, Long limit) { List<Comment> comments = lastParentCommentId == null || lastCommentId == null ? commentRepository.findAllInfiniteScroll(articleId, limit) : commentRepository.findAllInfiniteScroll(articleId, lastParentCommentId, lastCommentId, limit); return comments.stream() .map(CommentResponse::from) .toList(); } public Long count(Long boardId) { return articleCommentCountRepository.findById(boardId) .map(ArticleCommentCount::getCommentCount) .orElse(0L); } } @Test void concurrencyCountTest() throws InterruptedException { Long articleId = 24L; int threadCount = 10; ExecutorService executorService = Executors.newFixedThreadPool(threadCount); CountDownLatch latch = new CountDownLatch(threadCount); for(int i = 0; i < threadCount; i++) { final Long writerId = (long) (1000 + i); executorService.execute(() -> { try { createComment(new CommentCreateRequest(articleId, "concurrency test", null, writerId)); } catch (Exception e) { System.err.println("Exception in thread: " + Thread.currentThread().getName() + " -> " + e.getMessage()); } finally { latch.countDown(); } }); } latch.await(); Long commentCount = restClient.get() .uri("/v1/comments/articles/{articleId}/count", articleId) .retrieve() .body(Long.class); System.out.println("최종 commentCount = " + commentCount); assertThat(commentCount).isEqualTo(threadCount); } 그런데 이렇게 했을때 맨 처음 실행을 하면 1개의 데이터만 삽입되고 나머지 9개는 소실이됩니다 그리고 한번더 실행하면 11개의 데이터가 저장되는데 맨처음 저장된 1개의 데이터 + 10개의 스레드가 저장한 10개의 데이터가 되어 11개가 됩니다. 여기서 문제가 article_comment_count 테이블에 데이터가 아예 없을때 10개의 스레드가 동시에 insert문을 날리려고해서 Duplicate entry '24' for key 'article_comment_count.PRIMARY' 이런 문제가 나오지 않나 생각이 듭니다만.. create 메서드에 @Transactional(isolation = Isolation.READ _COMMITTED)로 격리 수준을 높여봤지만 여전히 문제가 해결되지 않습니다. 혹시 제가 잘못 이해한 부분이 있을까요? 그리고 동시성 문제를 해결하려면 어떻게 해야할까요? gpt에 물어보거나 구글링해서 찾아봐도 해결이 되지 않아서 질문드립니다!

  • java
  • mysql
  • spring-boot
  • kafka
  • redis
김규혁 댓글 2 좋아요 0 조회수 244

인기 태그

인프런 TOP Writers

주간 인기글