• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

repository 트랜잭션 관련 질문

24.05.29 22:11 작성 조회수 126

2

안녕하세요

스프링으로 개발하고 있는 뉴비인데, 최근에 golang에 흥미가 생겨서 강의를 듣게 되었습니다.

이제 강의를 다 수강하고, gorm을 사용해서 db연결까지 해보려고 하는데, 궁금증이 생겨서, 강의의 범위를 벗어나지만 질문드리려고 합니다

스프링에서는 서비스 계층에 트랜잭션을 선언후 그 안에서 벌어지는 db관련 로직들을 원자적으로 실행할 수 있습니다. 트랜잭션의 전파를 통해, 서비스에서 다른 서비스를 호출해도 하나의 트랜잭션으로 묶을 수 있고요.

 

gin에서 동일한 결과를 내고 싶은데, 제가 찾은 방법 2가지가 있습니다

  1. DB 객체를 서비스에서 필드로 보관하면서 repository를 호출할때마다 인자로 DB에 트랜잭션을 적용해 를넘겨준다 (또는 트랜잭션을 받는 wrapper function을 사용한다) => 코드작성량이 ㄷㄷ

  2. repository에 임시적인 WithTransaction(tx)메서드를사용해, repository의 DB가 임시로 트랜잭션을 바라보게한다. =>싱글톤에서 말도 안되는 방법인거 같은데 gpt가 강추함

  3. 컨텍스트에 트랜잭션 DB객체를 담아 모든 곳에서 꺼내 사용한다 => gpt, google 모두 권장되지 않는 방법이라고 함

3줄요약

전부다 다 뭔가 이상한거 같아서, 실무에서 golang을 사용하면 서비스에서 다수의 DB접근이 필요하고, 이를 하나의 트랜잭션으로 관리해야될 때 어떻게 트랜잭션을 처리하는지 궁금합니다!!

 

 

 

답변 1

답변을 작성해보세요.

0

July님의 프로필

July

지식공유자

2024.05.29

안녕하세요 😆 먼저 해당 강의 내용에서 더 나아가서 질문을 주셔서 너무 감사드립니다.

저는 이렇게 강의 내용을 넘어서는 질문이 들어오면 너무 흥미롭게 보고 있어서 기분이 좋네요.

 

일단 MySQL을 기준으로 설명을 드릴게요. 현재 실무에서도 약 100억건의 데이터를 MySQL로 이관하는 작업을 진행했어 가지고, 해당 DB가 좀 더 정확한 설명이 될 꺼 같습니다.

  • 이와 관련해서도 강의를 준비중에 있으니 기회가 되신다면 찾아주세요!! 😆

    먼저 저 같은 경우에는 MySQL을 다루는 경우에 있어서 Session 이라는 형태를 사용하지 않습니다.
    제가 Java부분에 대해서는 잘 알지 못하지만 JPA을 사용하시면 MongoDB든지 MySQL이든지 세션을 통해서 tx를 전송하는 걸로 알고 있어요.

  • 이 부분은 제가 틀릴 수도 있습니다.

 

또한 보통 이렇게 다수의 Tx를 다루는 방법은 어떤 쿼리냐에 따라 다르겠지만, INSERT 의 경우에는 prepared 또는 Bulk를 사용하게 됩니다.

 

질문자 분께서 질문을 해주신 내용중에 서비스에서 필드로 보관하면서 repository를 호출하여 트랜잭션을 넘겨준다.

 

해당 내용은 일단 잘못되었습니다. 서비스는 반드시 repository에서 데이터를 받아서 처리하는 주체가 되어야 합니다. 무언가 가공한 데이터를 넘기게 되면 기본 구조가 무너지게 됩니다.

  • 일부 상황에 따라서는 가능하겠지만 지양해야 하는 방식이라고 생각을 합니다.

 

2번 내용이 아마 세션에 관한 내용인거 같아요.

세션을 사용하게 되면, 세션간 Raw에 대한 접근이 불가능 합니다.

이는 이제 Exclusive Lock, Search Lock 등등 다양한 이름으로 불리게 되는데, 이러한 경우에 있어서 병목 현상이 발생을 하거나 DeadLock까지 발생을 하여 Tx가 실패를 하는 경우가 많습니다.

그래서 어디까지나 저의 경우지만 저는 실무에서 왠만하면 세션을 사용하지 않습니다.

 

3번 같은 경우에는 제가 JAVA에 대한 지식이 부족하여 내용을 이해하지 못하였네요 ㅠㅠ

 

어느정도 질문에 대한 답변을 해드린거 같고, 마지막 요약에 대한 답변을 해보자면 다음과 같습니다.

  • INSERT를 기준으로 설명 드리겠습니다.

 

일단 제가 실무에서 관리하는 서비스의 구성도는 다음과 같습니다.

  1. 일단 API 서버 (제가 관리합니다.)

  2. 데이터 집계 모듈 (거래량을 관리 합니다.)

  3. 데이터 수집 모듈 (제가 관리 합니다.)

 

이러한 구조에서 2번 항목은 Update가 많이 발생을 합니다.

  • 심하면 100 QPS까지도 발생을 하기도 합니다. 매우 드물지만

 

3번 항목은 INSERT, UPDATE가 주로 발생을 합니다.

 

이러한 구성도에서 사실 1,2,3번이 서로 같은 TX를 공유 할 수는 없겠죠

그래서 각각 따로 TX를 전송을 합니다.

이떄 session을 사용하냐 안하냐로 갈리게 되는데, 일반적으로 session을 사용하면 정합성이 더 보장이 될 겁니다.

  • 여러개의 TX중에 하나만 실패해도 모두 실패로 간주 할 것이기 떄문에

 

하지만 여러 서비스에서 Session을 사용을 하려면 TX에 대한 격리 단계를 수정해야 하는 필요가 있어요.

이는 MySQL에서 다루는 Lock이라는 개념을 수정해야 하기 때문이죠

 

이렇게 격리 단계를 수정하게 되면, 일반적으로 성능에 큰 영향이 가게 됩니다.

그래서 저는 따로 수정은 하지 않았고 최대한 쿼리에서 최적화를 진행을 하였습니다.

앞서 말한 Prepared Query, Bulk를 주로 사용을 하고 있습니다.

상황에 따라서는 Load Data라는 방법도 있는데, 이는 실제 모듈이나 서버에서는 사용이 되지 않는 형태이기는 합니다.

 

쩃든 결론은 그냥 Prepared, Bulk를 사용하라가 제가 말씀드리고 싶은 내용이 될꺼같네요.

제가 혹시 질문을 잘 이해하지 못하였다면, 다시 질문 남겨주시면 검토하고 답변을 해보도록 하겠습니다.

감사합니다!

김지헌님의 프로필

김지헌

질문자

2024.05.31

정성스런 답변 정말 감사드립니다!!

 

제가 이해한 바로는, 한 스레드(고루틴?)에서 db에 접근할때마다 하나의 트랜잭션 내에서 실행하는게 아니라, 각각의 db 접근마다 auto commit으로 동작하게 코드를 짜신다는거 같은데, 제가 이해한 게 맞을까요?

 

그렇다고 해도 , 무조건 원자성을 유지해야되는 (ex 상품구매 =>재고감소 => 결제 ) 상황에서는 하나의 트랜잭션으로 묶어야겠죠? 아님 다른 방법이 있을까요?

 

July님의 프로필

July

지식공유자

2024.05.31

네네 어디까지사 제 기준으로 설명을 드린거니깐 상황에따라서 하나의 tx로 묶어야한다면 묶으시면 됩니다.

 

단지 제가 말씀드린 주의 할 점들을 고려해서 구성해주시면 될꺼 같아요.

김지헌님의 프로필

김지헌

질문자

2024.05.31

답변 감사합니다

다음 강의 기대하겠습니다

채널톡 아이콘