티스토리 뷰

질문 피드백

  1. 클래스 단위의 @Transactional 사용

    저는 개인적으로 클래스 레벨에 Transactional을 사용하는 것을 선호하지 않습니다. 트랜잭션은 굉장히 비용이 큰 작업이고, 불필요한 트랜잭션으로 성능이 낮아지는 문제도 있지만 더 큰 문제는 의도되지 않은 트랜잭션으로 인해 데드락이 발생할 수 있다 라는 것이 생각입니다. 실제 현업에서 대규모 트래픽을 처리하기 위해 데드락이 발생하지 않도록 설계하는 것이 굉장히 중요한데요. 개발하는 과정에서도 이 기능에 데드락이 발생할 요소가 있을까? 트랜잭션을 꼭 잡아야 가능한 기능일까? 구조를 변경하여 락이 필요 없는 구조로 바꿀 수 없을까? 에 대해 끊임없이 고민하게 되는데, 이러한 고민을 하는 시점이 저는 메서드에 Transactional 처리를 하는 시점에 가장 많이 하는 것 같습니다. 또한 클래스마다 트랜잭션과 격리레벨이 정해지는 것이 아닌 기능(메서드)단위로 처리되기 때문에 클래스에 적용하여 공통으로 처리할 필요가 있을까? 라는 의문이 들었습니다 ㅎㅎ

  1. Q1. 요금 관련해서 로그인 사용자와 비로그인 사용자도 모두 접근 가능하게 해야 하더라구요. 이때, 방법을 잘 몰라서 지금은 null로 보내주고 null인지 체크를 하게 만들었는데ㅠㅠ null을 보내주는게 마음에 조금 걸려서요. Service 단에서 로그인 여부를 체크해서 다른 메소드를 불러주게 되어 있어서 null 자체를 넘길 수 밖에 없었어요. 나이 정보 때문에 이렇게 구성하게 되었는데, 혹시 더 좋은 방법이 있을지 궁금합니다.

    A1. 하나의 메서드에서 로그인에 따라 다르게 처리될 경우 지금과 같은 분리처리는 어쩔 수 없을 것 같습니다 Null Check 자체가 로그인을 확인하는 것이 명시적이지 않은 것이 문제라면, isLogin과 같은 기능을 추상화하여 로그인한 유저와 로그인하지 않은 유저로 나누어 처리할 수 있는 방법이 있을 것 같습니다(혹은 Role에 대한 역할을 추가해봐도 되겠네요!!)이러한 처리는 일종의 Null Object Pattern과 같은 개념으로 해당 키워드로 검색해보면 좋을 것 같아요 ㅎㅎ조금 더 나아가서 API 설계 시, 누구나 접근할 수 있는 API와 로그인 한 유저만 접근할 수 있는 API 2개로 나누어 클라이언트에서 분기처리 하는 방법도 있답니다 :)

  2. Q2. 지금 lines/{id}, lines/{id}/sections/~~ 이런식으로 체크하다 보니 lines와 관련된 모든 로직은 lineId에 대한 검증로직을 추가해주어야 하더라구요. 모든 곳에 중복되서 validate 가 들어가는데 혹시 더 개선하는 방향이 있나요? 아니면 보통 이렇게 매 메소드 호출시 Service 단에서 중복 검증로직을 추가해주는 편인가요?

    A2. line과 연관이 있는 부분이라면 같이 검증을 해주셔야 할 것 같습니다!방법은 lineId와 sectionId를 같이 조회하는 방식 등으로 처리될 수도 있고, 다른 로직이 있을 수도 있을 것 같습니다 :)

학습로그

[DB] 트랜잭션

데드락

  • 두 개 이상의 작업이 서로 상대방의 작업이 끝나기 만을 기다리고 있기 때문에 결과적으로 아무것도 완료되지 못하는 상태
  • 참고링크 - 데이터베이스에서 데드락
  • 락이 걸려있는 것들 끼리 자료수정이 같이 일어나면, lock 되어 있는 상태에서 데드락이 발생할 가능성이 있는 것 같음
  • 격리수준이 높을 수록 락의 가능성이 높아짐?!
  • 두 개 이상의 트랜잭션이 각각 자신의 데이터에 대하여 락을 획득하고 상대방 데이터에 대하여 락을 요청하면 무한 대기 상태에 빠질 수 있는 현상
  • 참고링크
  • 한 테이블에서 순서없이 갱신하면 교착상태가 발생하기 쉽다. 갱신을 직렬화하면 동시성은 떨어지지만 교착상태를 회피할 수 있다.

격리수준(isolation level)

  • @Transactional에 걸리는 격리수준의 기본설정은 DB의 기본 설정을 따른다.
  • READ_UNCOMMITTED: 가장 낮은 수준의 격리수준
  • READ_COMMITTED: dirty read 를 막아줌. commit 된 내용만 read 가능(트랜잭션 시, 다른 부분이 커밋되면 변경된 내용으로 읽기 가능)
  • REPEATABLE_READ: 세번째 수준의 격리수준. 처음 읽기 시작할 시점의 커밋된 내용만 read 가능
  • SERIALIZABLE: 가장 높은 격리수준으로 read에도 lock을 생성

전파속성(propagation)

  • 이제 트랜잭션을 시작하거나 기존 트랜잭션에 참여하는 방법을 결정하는 속성
  • REQUIRED: default. 활성된 트랜잭션이 있다면 참여하고, 그렇지 않다면 새로운 트랜잭션을 발생
  • SUPPORTS: 활성된 트랜잭션이 있다면 참여하고, 그렇지 않다면 트랜잭션 없이 진행
  • MANDATORY: 활성된 트랜잭션이 있다면 참여하고, 그렇지 않다면 예외를 발생
  • NEVER: 활성된 트랜잭션이 있다면 예외를 발생
  • NOT_SUPPORTED: 활성된 트랜잭션이 있다면 보류시킴
  • REQUIRES_NEW: 활성된 트랜잭션이 있다면 보류시키고 새 트랜잭션을 생성
  • NESTED: 이미 진행중인 트랜잭션이 있다면, save point 를 마크함. 그리고 중첩 트랜잭션을 실행
  • propagation - bealdung blog

태그

  • DB, 트랜잭션, 데드락

[Spring] @Bean, @Component

@Bean VS @Component

  • @Bean
    • 외부 라이브러리들을 Bean 으로 등록하고 싶은 경우에 사용
    • 메소드 단위, 어노테이션 단위에 붙일 수 있음, 클래스 단위는 불가함
    • 인스턴스를 생성하는 메소드를 만들고 해당 메소드에 @Bean을 선언하여 Bean 으로 등록
    • @Bean@Configuration이 붙은 DI 설정용 클래스에서 주로 사용하는 것으로, 메소드를 이용해서 빈 오브젝트의 생성과 의존관계 주입을 직접 자바코드르 작성
    • @Configuration 자바 클래스에서 정의한 빈과 XML 에서 정의한 빈은 얼마든지 서로 참조가 가능
    • 스캔할 필요 없이 빈 등록이 빠르게 이루어짐
    • 스프링 공식문서 Bean
  • @Component
    • 개발자가 직접 컨트롤이 가능한 Class 들의 경우
    • 빈으로 등록될 후보 클래스에 붙여주는 일종의 마커
    • @Component가 있는 클래스를 찾아서 빈으로 등록하려면 빈 스캔 기능을 사용하겠다는 어노테이션 정의가 필요
    • 컴포넌트 어노테이션이 있는 클래스를 찾아서 빈으로 등록하는 건 부담가는 작업
    • 특정 패키지 아래서만 찾도록 기준이 되는 패키지를 지정하는 것이 좋음 -> @ComponentScan을 사용
    • @Autowired로 찾을 대상이 두 개 이상인 경우(같은 타입의 빈이 두개 이상인 경우), 이름을 기준으로 빈을 등록해야하기 때문에 빈의 아이디가 중요해짐
    • 이럴때, 클래스 이름 대신 빈의 아이디로 사용하고 싶다면 @Component("userDao")처럼 애노테이션에 이름을 넣어주면 됨 (토비의 스프링:678pg)
    • 스프링 공식문서 Component
    • @Configuration@Bean이다.
  • 둘 간의 큰 성능차이는 없는 것 같다. Bean 으로 등록하는 방식이 더 오래된 방식 같으며, 외부 라이브러리에는 @Component를 사용할 수 없어 인스턴스를 만들어 @Bean으로 등록
  • bean, component
  • 토비의 스프링 7장

@Resource VS @Autowired

  • 빈 등록
  • @Autowired: 필드의 타입으로 기준으로 빈을 찾음
  • @Resource: 필드의 이름 기준으로 빈을 찾음

태그

  • 빈 등록

[Test] @Nested

@Nested 계층적 테스트 구조

태그

  • nested, 테스트

피드백 - 2

질문과 답변

데드락을 줄이는 방법

  1. 최대한 락을 줄인다.
  • 불필요한 락은 전부 없애고, 필요한 락이 있더라도 락이 안잡도록 구조를 잡을 수 없을까 고민한다.
  • ex. 제약조건 확인과 같은 경우 별도 테이블을 분리하여 UNIQUE를 활용
  • 락을 없앨 수 없다면 락을 잡는 시간을 최대한 줄인다.
  1. 트랜잭션 내부 처리 순서를 일치시킨다.
  • 송금기능을 구현한다 했을 때, A 트랜잭션은 출금->입금 순으로 진행하고 B 트랜잭션은 입금->출금 순으로 진행한다면, 동일한 처리순서를 가지도록 수정한다.
  • 로직 순서에 의한 데드락을 방지한다.
  1. 락은 Row Lock만 가지도록 설계한다.
  • 하나의 로우에 여러 유저가 접근할 일을 최대한 줄인다.
  • 유저 별 데이터가 존재한다면 데드락이 발생할 확률이 거의 없어지고 발생한다하여도 장애가 다른 유저에게 전파되지 않는다.
  1. 여러 유저가 하나의 로우에 접근할 일이 있다면, 락을 잡지 않는 방식으로 작성한다.
  • 락을 잡지 않는 쿼리를 보낸 후, affected rows와 같은 응답을 가지고 처리한다.
  • ex) UPDATE group SET count = count + 1 WHERE id = {id};
  • 이러한 변화에 따른 비즈니스 로직의 변경이나, 불필요한 데이터가 생기는 문제는 있기 때문에 적절한 트레이드 오프가 필요하다.
  1. 불필요한 인덱스를 제거하고, 카디널리티가 높은 데이터를 인덱스로 잡는다.
  • 적은 레코드를 스캔하면 데드락의 확률이 낮아진다.
  1. 최대한 격리레벨을 낮춘다.
  • 격리레벨에 따라 락의 메커니즘이 다르다.
  • 대용량 분산처리를 하는 서비스의 경우 SERIALIZABLE은 꿈도 꾸지 않는다.

핵심은 데드락은 버그라는 생각을 버려야하고, 동시성에 대한 잠금처리를 위해 선택한 기술 중 하나라고 생각하는 것이 좋습니다.
동일한 데이터에 대해 접근하는 것에 대한 처리를 위해 데드락을 의도했다! 라는 관점으로 접근해보면 좋을 것 같습니다!

https://stackoverflow.com/questions/46230555/why-i-shouldnt-use-repeatable-read-with-locking-reading-select-for-update/46231651#46231651

데드락을 예방하는 방법으로 READ-COMMITTED 격리레벨을 사용 후 데드락 발생 시 재시도를 하도록 하는 것을 추천하는 내용입니다 ㅎㅎ
데드락은 피할 수 없기 때문에 같이 즐겨보아요..! T.T

[Spring] Lite Bean Mode

빈 등록 - Lite Bean Mode

  • 참고 블로그

  • Spring docs - Bean

  • @Configuration 대신 @Component 어노테이션을 붙이면 Lite mode 로 빈이 생성됨

  • 컨테이너에 의해 일반 팩토리 메소드로 관리됨 (XML 설정으로 선언하는 것과 비슷) 제약조건등이 관리되지 않음

  • 내부 빈 참조가 지원되지 않으나 @Bean-method가 다른 @Bean-method를 lite mode 로 작동시킴

  • CGLIB 프록시 객체가 아닌 일반 객체로 관리됨(@Configuration을 사용하면 CGLIB 바이트 코드를 조작하여 메소드 호출이 1회만 일어나게 만듦)

    Bean methods in lite mode will be treated as plain factory methods by the container (similar to factory-method declarations in XML), with scoping and lifecycle callbacks properly applied.
    The containing class remains unmodified in this case, and there are no unusual constraints for the containing class or the factory methods.
    In contrast to the semantics for bean methods in @Configuration classes, 'inter-bean references' are not supported in lite mode.
    Instead, when one @Bean-method invokes another @Bean-method in lite mode, the invocation is a standard Java method invocation;
    Spring does not intercept the invocation via a CGLIB proxy.
    This is analogous to inter-@Transactional method calls where in proxy mode, Spring does not intercept the invocation — Spring does so only in AspectJ mode.

  • spring docs - configuration vs lite

  • @Configuration에서 @Bean을 관리하면, 동일한 @Bean 메소드가 Java 호출을 통해 실수로 호출되는 것을 방지하여 lite mode 에서 찾기 힘든 버그를 줄일 수 있음

CGLIB

  • spring docs - CGLIB

    CGLIB proxies intercept only public method calls! Do not call non-public methods on such a proxy.
    They are not delegated to the actual scoped target object.

태그

  • 빈 등록, Configuration, Bean, Component, CGLIB

[DB] Transaction

트랜잭션

  • 단일 쿼리문은 자체로 트랜잭션이 적용됨
  • 성공하면 커밋, 실패하면 롤백

태그

  • transaction

[DB] Cardinality

카디널리티(SQL statements)

  • wikipedia
  • 카디널리티가 낮을수록 더 많은 중복요소
  • 카디널리티가 높다 -> 중복 요소가 적다
  • 고유성을 가지는 tuple 의 데이터 값들을 의미
  • stackoverflow cardinality

태그

  • DB, cardinality
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
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
글 보관함