티스토리 뷰

결과 : 
네트워크 호출 한 번으로 다중 쿼리 결과를 Mybatis Multi ResultSet으로 받아오기 

이슈 : 

서비스 도중 회원정보 조회가 수 십초가 걸리는 현상이 있었음다. 
비록 순간 짧은 시간이었지만, 이로 인해 다른 도메인 서비스에서는 timeout으로 500에러가 발생 

 

원인 : 

회원 조회 부분에 여러 쿼리를 비동기 호출로 가져오고 있었음.
모니터링 도구로 확인해 보니 쿼리 호출 자체가 엄청 느린것을 확인할 수 있었음.
db connection 하는 도중 tread가 부족하여connection 될 떄 까지 대기하고 있었음.   
아무래도 비동기 호출이 2번만 이루어 지더라도 1분에 회원 조회가 10000번 호출되면 
필요한 thread수는 많아 질 수 밖에 없음. 

조치 :
보통 쿠버네티스 pod이 cpu 15% 정도 사용하는 것을 확인하고,
스펙을 cpu성능은 낮추고 pod 개수를 조금 더 늘렸다. 

위 조치는 TA에서 조치한 부분이며, 나는 코드적으로 해결할 수 있는 방법을 찾아 보았음. 
현재 프로젝트 구조상 성능을 위해서는 데이터베이스와 네트워크 호출이 적을 수록 좋다고 판단을 하였음.

그래서 네트워크 호출이 한 번만 호출 되도록 다수 회원 조회 쿼리들을 단일 프로시저로 묶어버리기로 하였음. 

 

위 그림을 통해서 보면 회원정보를 조회하기 위해서는 3번의 커넥션이 호출되고 있다. 

물로 일반적인 경우 CQRS패턴을 통회 조회,읽기 전용 쿼리가 있어 
여러번 호출이 성능상 큰 영향이 없을 수도 있지만 서비스 구조가 그렇지 않은 경우는 
커넥션 1번이 비싼 자원일 수도 있다. 

 

그래서 위 그림처럼 1번의 네트워크 연결로 모든 조회를 다 변경해 보기로 했다.



Mybatis Multi ResultSet

단일 프로시저 호출로 변경하기로 결정하였지만 문제가 하나 발생했다. 

 

DECLARE  @인증_휴대폰_ 번호
DECLARE  @결제_번호

 

SELECT TOP 1 @인증_휴대폰_ 번호 = Phone
FROM 인증_테이블
WHERE ID = 'kakao'


SELECT TOP 10 @결제_번호 = Phone
FROM 결제_테이블
WHERE ID = 'kakao'

SELECT 

              , @인증_휴대폰_ 번호

              , @결제_번호  < 불가능! (조회 결과가 여러 개이기 때문에!) 
FROM 회원_테이블
WHERE ID = 'kakao'

 

이렇게 했을때 @결제_번호는 조회 결과가 여러개 이기 때문에 조회 자체에 에러가 발생한다. 



그렇다면 단일 조회로 변경할 수 있는 방법은 무엇이 있을까?
1. LEFT JOIN을 통해 결과 자체를 LIST로 가져오고 코드단에서 재조합한다. (select 결과 1개)
2. @결제_번호를 ","(콤마)를 이용해서 이어 붙여 하나의 결과로 조회 한다.

     그리고 코드단에서 다시 리스트로 변환한다. (select 결과 1개)
3. select 결과를 여러개 가져오고 Mybatis Multi ResultSet을 이용한다. (select 결과 여러 개)

 

1번, 2번도 충분히 깔끔하게 조합할 수 있는 여러 방법들이 있었지만,

나는 최대한 코드단에서 코드가 변경되는 경우는 없었으면 했다.
운영에서는 코드의 변화가 많다면 그 만큼 사이드 이펙트 범위도 넓어 지기 때문이다.
또한 회원정보 조회는 사용되는 Consumer(소비자)들이 많다.

검수(QA)과정이 있더라도 최대한 코드단에서 작업하는 경우는 피하고 싶어
"3. select 결과를 여러개 가져오고 Mybatis Multi ResultSet을 이용한다. (select 결과 여러 개)" 방식을 선택하였다.


MyBatis (XML)

조회 하는 쿼리 순서에 맞게 resultMap을 작성해 주면 된다.

  <resultMap id="userResultMap" type="User">
		...
  </resultMap>

  <resultMap id="CertificationResultMap" type="Certification">
		...
  </resultMap>

  <resultMap id="PayResultMap" type="Pay">
		...
  </resultMap>


<select id="findUser" resultMap="userResultMap,CertificationResultMap,PayResultMap">

    SELECT *
    FROM 회원_테이블
    WHERE 아이디

    SELECT *
    FROM 휴대폰_인증_테이블
    WHERE 아이디

    SELECT *
    FROM 결제_테이블
    WHERE 아이디

</select>

 

 

Repository (JAVA)

public List<List<Object>> findUser(String id)

이후 사용은 조회 순서에 따라 조회 할 수 있다.
list.get(0) - 회원 정보

list.get(1) - 휴대폰 인증 정보

list.get(2) - 결제 정보



APM 모니터링 툴을 이용하여 네트워크가 한 번만 호출된다는 것을 확실히 확인하자~!


마무리 

클린코딩 관점에서 쿼리 결과를 여러번 조회해 오고 이것을 Object로 받는 것이 좋은 방법 인지는 잘 모르겠다. 

유지보수 관점에서도 다른 사람들은 List안에 결과값이 무엇인지 쿼리 혹은 프로시저를 확인해야 알 수 있다. 

회사 프로님의 조언으로는 LEFT JOIN (LEFT JOIN을 통해 결과 자체를 LIST로 조회) 하는 방식을 이용하면
Result Map에서 결과는 User 엔티티 하나이며 그 하위 엔티티 들은 자동으로 매핑되는 방식이 있다고 말씀해 주셨다.
하지만 이 방법은 찾지 못했다. 

LEFT JOIN 방법이 가능하다면 코드의 의미도 명확해 지고 가독성도 좋아지기 때문에 가장 좋은 방식이라고 생각한다. 
추후 LEFT JOIN 방식도 찾아 보고 글을 작성해야 겠다.

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