해결된 질문
작성
·
181
2
안녕하세요
스프링으로 개발하고 있는 뉴비인데, 최근에 golang에 흥미가 생겨서 강의를 듣게 되었습니다.
이제 강의를 다 수강하고, gorm을 사용해서 db연결까지 해보려고 하는데, 궁금증이 생겨서, 강의의 범위를 벗어나지만 질문드리려고 합니다
스프링에서는 서비스 계층에 트랜잭션을 선언후 그 안에서 벌어지는 db관련 로직들을 원자적으로 실행할 수 있습니다. 트랜잭션의 전파를 통해, 서비스에서 다른 서비스를 호출해도 하나의 트랜잭션으로 묶을 수 있고요.
gin에서 동일한 결과를 내고 싶은데, 제가 찾은 방법 2가지가 있습니다
DB 객체를 서비스에서 필드로 보관하면서 repository를 호출할때마다 인자로 DB에 트랜잭션을 적용해 를넘겨준다 (또는 트랜잭션을 받는 wrapper function을 사용한다) => 코드작성량이 ㄷㄷ
repository에 임시적인 WithTransaction(tx)메서드를사용해, repository의 DB가 임시로 트랜잭션을 바라보게한다. =>싱글톤에서 말도 안되는 방법인거 같은데 gpt가 강추함
컨텍스트에 트랜잭션 DB객체를 담아 모든 곳에서 꺼내 사용한다 => gpt, google 모두 권장되지 않는 방법이라고 함
3줄요약
답변 1
0
안녕하세요 😆 먼저 해당 강의 내용에서 더 나아가서 질문을 주셔서 너무 감사드립니다.
저는 이렇게 강의 내용을 넘어서는 질문이 들어오면 너무 흥미롭게 보고 있어서 기분이 좋네요.
일단 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
를 기준으로 설명 드리겠습니다.
일단 제가 실무에서 관리하는 서비스의 구성도는 다음과 같습니다.
일단 API 서버 (제가 관리합니다.)
데이터 집계 모듈 (거래량을 관리 합니다.)
데이터 수집 모듈 (제가 관리 합니다.)
이러한 구조에서 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
를 사용하라가 제가 말씀드리고 싶은 내용이 될꺼같네요.
제가 혹시 질문을 잘 이해하지 못하였다면, 다시 질문 남겨주시면 검토하고 답변을 해보도록 하겠습니다.
감사합니다!
정성스런 답변 정말 감사드립니다!!
제가 이해한 바로는, 한 스레드(고루틴?)에서 db에 접근할때마다 하나의 트랜잭션 내에서 실행하는게 아니라, 각각의 db 접근마다 auto commit으로 동작하게 코드를 짜신다는거 같은데, 제가 이해한 게 맞을까요?
그렇다고 해도 , 무조건 원자성을 유지해야되는 (ex 상품구매 =>재고감소 => 결제 ) 상황에서는 하나의 트랜잭션으로 묶어야겠죠? 아님 다른 방법이 있을까요?