안녕하세요. 한국신용데이터 관리서비스팀 Tony(정원재) 입니다.
저는 입사 후 캐시노트 서비스의 안정화를 위한 작업을 계속해오고 있습니다. 🙂 오늘은 그 중에서 가장 큰 작업 중 하나였던 카드 매출 데이터의 DB를 분리한 과정을 공유드리려 합니다.
문제 정의
22년 8월 한국신용데이터에 입사하기 전 흥미로운 이야기를 접하게 되었습니다.
“우리 서비스 DB CPU가 90을 넘길 때가 있대.”
DB를 무척 아슬아슬하게 사용하는 회사구나 싶더군요. 이와 관련하여 서비스 문제가 많이 발생하고 있을 것 같은 호기심이 생겼습니다. (이런 거에 승부욕 생기지 말란 말이다…🫠) 물론 험난한 길이 예상되는 순간이기도 했습니다.
시간이 흘러 입사하게 된 후 파악한 DB의 문제는 생각했던 것보다 심각한 상황이었습니다.
DB CPU 이슈
캐시노트의 핵심 데이터는 카드 매출 데이터입니다. 카드 매출 데이터는 외부 소스로부터 수집해오고 있었고요. 이를 수집해오는 시간 동안 DB CPU가 90% 넘어설 때가 생기고, 전반적으로 높은 CPU 수치를 보이고 있었습니다.🔥 DB 에서 지연이 발생하는 부분을 살펴보니 수집하는 과정과 수집 후 카드 매출 거래 정보를 생성하는 과정에서 DB CPU가 오르락 내리락 하고 있었습니다.
캐시 노트 DB CPU
모든 데이터가 하나의 DB에
캐시노트의 메인 DB가 서비스 정보, 각종 수집 데이터 등 모든 데이터를 처리하고 있었습니다. 그러다보니 관리 상의 문제도 생기고, DB의 부하도 함께 감당해야 하는 상황이었습니다.
크기가 너무 커진 카드 매출 데이터
전체 DB용량이 35TB였는데, 그 중 카드 매출 관련 테이블이 30TB를 넘게 차지하고 있는 상황이었습니다. 그리고 카드 매출의 원데이터인 승인 데이터와 매입 데이터는 파티션 분리가 되어 있지 않은 10TB에 가까운 테이블들로 되어 있었습니다.
OS나 보안 패치 등의 관리가 되지 않고 있음
위에 언급한 문제들과 연관 된 부분으로 너무 커진 데이터로 인하여 DB 재시작이 어려운 상황이었습니다. 그러다보니 DB 관리가 전혀 이루어지지 않고 있었습니다.
EOL이 다가오고 있음
사용 중인 DB가 RDS Postgresql 10.X 버전인데, 22년 11월 EOL이었습니다. 조사 당시 기준으로는 2달 정도 남은 상태였고, 현재는 이미 지원이 종료가 된 상태로 AWS에서도 지원 종료 일정을 전달 받은 상태입니다.
Postgresql 버전 정책 (출처: https://www.postgresql.org/support/versioning/)
원인 분석 및 솔루션 찾기
많은 문제를 안고 있기는 했지만, 서로 연결이 되어 있는 부분이 많았습니다. 그래서 각 문제들의 원인을 찾아보며 가장 효과적인 솔루션을 찾아 보기로 했습니다.
데이터 분리, DB 패치, 버전업은 잠시 잊어두기로 했습니다. 추가적인 분석이 필요하기 보다는 해야할 일이 정해져 있는 일이었기 때문입니다. DB를 안정화 시킨 후 적절한 시기를 찾아서 해결하면 될 것 같았습니다.
일단 CPU 부하와 용량 문제를 수습하는데 집중하기로 합니다.
DB CPU 부하 분석
DB CPU에 부하를 발생시키는 것이 수집기와 후처리 로직이라는 것은 전달받은 상태지만, 무엇이 그렇게 만드는지 궁금했습니다.
만약 처리 과정에서 개선이 불가능한 상황이라면, RDB를 떠나 다른 수단을 찾아야 할 필요가 있을 것이라 생각했습니다. 하지만 적절한 개선으로 부하를 낮출 수 있다면 RDB를 벗어날 필요는 없었기 때문에 이 부분을 먼저 확인했습니다.
수집기의 경우 insert 이외에 별도의 가공처리 등이 없기에 수집 후 처리 부분에서 개선할 부분을 찾아야 했습니다.
다행스러운 점은 수집 후 처리 부분에 개선점이 존재했습니다. 뜯어보니 계산로직, 서브쿼리, 다수의 조인, update와 insert의 혼합으로 이루어진 복잡한 쿼리가 수행되고 있었습니다. 이 부분들을 잘 쪼개고 바꾸면 부하를 꽤 감소시킬 수 있을 것 같았습니다.
Get Tony (정원재)’s stories in your inbox
Join Medium for free to get updates from this writer.
다만, 이 과정을 개선하기에는 데이터 정합성을 확인과 테이블에 변경이 필요해 시간을 들여 차근차근 진행해야 했습니다.
그렇게 여유있게 진행을 할 수 있는 상황은 아니었기에, 이 부분을 개선하는 것은 후순위로 미루기로 했습니다. 이보다는 더 빠른 해결책을 찾아야 했습니다.
용량 문제 분석
용량에 대한 부분 이미 조사된 내용이 있었기에 따로 조사할 것은 없었고 어떻게 해결할 것인가를 결정하면 되는 상태였습니다.
서비스 데이터, 회원 데이터, 수집 데이터 등이 고르게 증가한 것이라면 바로 DB를 분리시키는 선택을 하게 되었을 것 같습니다. 그러나 수집 데이터가 대부분의 용량을 차지하고 있었기에 DB 분리 말고 다르게 용량을 확보할 수 있는지 고민을 하게 되었습니다.
첫번째 살펴 본 부분은 로우 데이터 성격인 승인/매입 데이터였습니다. 서비스에서 사용하기 위해 생성한 거래 데이터가 있음에도 이 데이터들이 필요한 이유가 무엇일까 궁금했습니다. 이 데이터들의 필요성을 다른 방식으로 채워주게 되면 20TB 용량을 확보하게 될 수 있었기 때문입니다. 이 후 DB 분리를 진행할 때의 부담도 줄일 수 있을 것 같았습니다. 또한 이 데이터는 데이터 플랫폼팀에서 별도로 관리하고 있는 데이터라 캐시노트의 DB에서 삭제를 하더라도 필요 시 조회가 가능한 부분이었습니다.
승인/매입 데이터가 사용되고 있는 부분을 살펴보니 최초 거래 데이터를 설계했을 때의 기능에서 벗어나는 추가 데이터 및 조건이 생기게 되며 이 부분을 승인/매입 데이터와의 join을 통해 해결한 것이 아닐까 하는 부분들이었습니다.
두번째로 살펴 본 부분은 오래된 데이터의 클랜징이었습니다. 서비스 운영에 필요한 데이터는 2년까지로 정해지며, 오래된 데이터의 클랜징이 가능해졌습니다. 이부분을 제거하는 것도 유의미한 용량 확보가 가능할 것 같았습니다.
이 2가지 작업을 완료하게 되면 DB 분리 작업을 급하게 진행하지 않을 수 있을 것 같았습니다. 먼저 시간을 벌고, 로직 개선을 통해 다른 부분의 문제를 해결하는 것이 가능할 것 같았습니다. 그래서우선적으로 진행을 해보려 진행 가능 여부를 DBA에게 확인해보았습니다.
하지만 DB 파라미터 설정 등의 이슈와 Vaccum 등의 이슈, 그리고 높은 CPU 수치를 가진 현재 DB에 컬럼이나 인덱스 추가 등의 조치를 취하는 것이 어려웠습니다. 클랜징 또한 Vacuum 이슈로 당장 적용이 어려운 상황이라 적용이 어려운 상황이었습니다.
카드 매출 데이터 분리
우선적으로 캐시노트 전체가 중단 되는 것을 막고, 다른 작업들을 할 수 있는 기반을 만들기 위해서 대부분의 이슈를 가지고 있는 카드 매출 데이터의 DB를 분리시키기로 결정했습니다.
카드 매출 데이터 분리 시 위험이 될 수 있는 부분들을 먼저 점검을 해보았는데 아래와 같았습니다.
- 개발 히스토리를 알고 있는 사람이 대부분 퇴사한 상태라, 데이터 사용처 조사 시 누락하는 부분이 많을 수 있음
- 테이블에 on delete cascade 가 걸려 있는 상태라 DB 처리에 많은 주의가 필요함
- 데이터 수집이 별도의 수집기를 통해 이루어 지고 있어 이슈 발생 시 롤백이 어려움
이 외에도 나중에 진행하며 알게 된 부분이지만, view나 function 등을 활용하고 있는 부분들이 많아 분리 전 처리에 놓치는 부분들이 존재했습니다.
코드 사전 조사
작업 방향을 결정하기에 앞서 어떤 방식으로 코드를 수정해야하는가를 확인하기 위해 조사를 시작했습니다.
쿼리 등이 도저히 손을 댈 수 없는 경우 로직 자체를 변경하여 개선작업을 먼저 진행해야 했기 때문에 가장 많은 기도가 필요했던 순간이었습니다.🙏
안타깝게도 처음 접하는 루비 온 레일즈라 코드를 탐색하는 것조차 서툴었지만, 그나마 친숙한 UI/UX를 가진 루비 마인을 통해서 조심스럽게 조사를 시작했습니다. 🔎
몇 번의 실험을 통해 루비 온 레일즈에서 사용하는 ActiveRecord 모델 class들의 usage만 찾는 것으로는 모든 사용처를 찾을 수 없다는 것을 확인했기에 두가지 단계로 나누어 조사를 시작했습니다.
- ActvieRecord 모델 class들의 usage를 탐색
- .card_sales_XXXX 라는 텍스트를 프로젝트에서 탐색
불행인지 다행인지 모르겠지만, 캐시노트 코드에는 Business라고 하는 God object가 존재하고 있어 대부분의 로직은 이 객체를 통해 처리하고 있습니다. 핵심 기능의 경우 이 객체를 통해 호출되는 로직을 찾아 처리가 가능했습니다. 그래서 class usage 탐색으로 나오지 않는 부분들도 Business의 카드 매출 관련 프로퍼티를 텍스트로 탐색이 가능했습니다. 이 방식으로 대부분의 사용처를 조사할 수 있었습니다. (이렇게 해서 못 찾는 건 자주 안 쓰는 기능이기를 바라면서…🥲)
의외의 조사 결과
조사 결과를 정리하던 중 재미있었던 부분은 이관 대상으로 정해두었던 테이블 3개에 자주 join해서 사용하는 테이블 몇 개를 이관대상에 추가했더니, 타 테이블과의 연결이 거의 존재하지 않았습니다. 제가 조사하고도 신기해서 잘못 찾은 건가 3번 정도 더 확인을 해보았지만 정말 없는게 맞아서 놀라웠습니다. 🎉
>> Part. 2에서 계속
안녕하세요. 한국신용데이터 관리서비스팀 Tony(정원재) 입니다.
저는 입사 후 캐시노트 서비스의 안정화를 위한 작업을 계속해오고 있습니다. 🙂 오늘은 그 중에서 가장 큰 작업 중 하나였던 카드 매출 데이터의 DB를 분리한 과정을 공유드리려 합니다.
문제 정의
22년 8월 한국신용데이터에 입사하기 전 흥미로운 이야기를 접하게 되었습니다.
“우리 서비스 DB CPU가 90을 넘길 때가 있대.”
DB를 무척 아슬아슬하게 사용하는 회사구나 싶더군요. 이와 관련하여 서비스 문제가 많이 발생하고 있을 것 같은 호기심이 생겼습니다. (이런 거에 승부욕 생기지 말란 말이다…🫠) 물론 험난한 길이 예상되는 순간이기도 했습니다.
시간이 흘러 입사하게 된 후 파악한 DB의 문제는 생각했던 것보다 심각한 상황이었습니다.
DB CPU 이슈
캐시노트의 핵심 데이터는 카드 매출 데이터입니다. 카드 매출 데이터는 외부 소스로부터 수집해오고 있었고요. 이를 수집해오는 시간 동안 DB CPU가 90% 넘어설 때가 생기고, 전반적으로 높은 CPU 수치를 보이고 있었습니다.🔥 DB 에서 지연이 발생하는 부분을 살펴보니 수집하는 과정과 수집 후 카드 매출 거래 정보를 생성하는 과정에서 DB CPU가 오르락 내리락 하고 있었습니다.
모든 데이터가 하나의 DB에
캐시노트의 메인 DB가 서비스 정보, 각종 수집 데이터 등 모든 데이터를 처리하고 있었습니다. 그러다보니 관리 상의 문제도 생기고, DB의 부하도 함께 감당해야 하는 상황이었습니다.
크기가 너무 커진 카드 매출 데이터
전체 DB용량이 35TB였는데, 그 중 카드 매출 관련 테이블이 30TB를 넘게 차지하고 있는 상황이었습니다. 그리고 카드 매출의 원데이터인 승인 데이터와 매입 데이터는 파티션 분리가 되어 있지 않은 10TB에 가까운 테이블들로 되어 있었습니다.
OS나 보안 패치 등의 관리가 되지 않고 있음
위에 언급한 문제들과 연관 된 부분으로 너무 커진 데이터로 인하여 DB 재시작이 어려운 상황이었습니다. 그러다보니 DB 관리가 전혀 이루어지지 않고 있었습니다.
EOL이 다가오고 있음
사용 중인 DB가 RDS Postgresql 10.X 버전인데, 22년 11월 EOL이었습니다. 조사 당시 기준으로는 2달 정도 남은 상태였고, 현재는 이미 지원이 종료가 된 상태로 AWS에서도 지원 종료 일정을 전달 받은 상태입니다.
원인 분석 및 솔루션 찾기
많은 문제를 안고 있기는 했지만, 서로 연결이 되어 있는 부분이 많았습니다. 그래서 각 문제들의 원인을 찾아보며 가장 효과적인 솔루션을 찾아 보기로 했습니다.
데이터 분리, DB 패치, 버전업은 잠시 잊어두기로 했습니다. 추가적인 분석이 필요하기 보다는 해야할 일이 정해져 있는 일이었기 때문입니다. DB를 안정화 시킨 후 적절한 시기를 찾아서 해결하면 될 것 같았습니다.
일단 CPU 부하와 용량 문제를 수습하는데 집중하기로 합니다.
DB CPU 부하 분석
DB CPU에 부하를 발생시키는 것이 수집기와 후처리 로직이라는 것은 전달받은 상태지만, 무엇이 그렇게 만드는지 궁금했습니다.
만약 처리 과정에서 개선이 불가능한 상황이라면, RDB를 떠나 다른 수단을 찾아야 할 필요가 있을 것이라 생각했습니다. 하지만 적절한 개선으로 부하를 낮출 수 있다면 RDB를 벗어날 필요는 없었기 때문에 이 부분을 먼저 확인했습니다.
수집기의 경우 insert 이외에 별도의 가공처리 등이 없기에 수집 후 처리 부분에서 개선할 부분을 찾아야 했습니다.
다행스러운 점은 수집 후 처리 부분에 개선점이 존재했습니다. 뜯어보니 계산로직, 서브쿼리, 다수의 조인, update와 insert의 혼합으로 이루어진 복잡한 쿼리가 수행되고 있었습니다. 이 부분들을 잘 쪼개고 바꾸면 부하를 꽤 감소시킬 수 있을 것 같았습니다.
Get Tony (정원재)’s stories in your inbox
Join Medium for free to get updates from this writer.
다만, 이 과정을 개선하기에는 데이터 정합성을 확인과 테이블에 변경이 필요해 시간을 들여 차근차근 진행해야 했습니다.
그렇게 여유있게 진행을 할 수 있는 상황은 아니었기에, 이 부분을 개선하는 것은 후순위로 미루기로 했습니다. 이보다는 더 빠른 해결책을 찾아야 했습니다.
용량 문제 분석
용량에 대한 부분 이미 조사된 내용이 있었기에 따로 조사할 것은 없었고 어떻게 해결할 것인가를 결정하면 되는 상태였습니다.
서비스 데이터, 회원 데이터, 수집 데이터 등이 고르게 증가한 것이라면 바로 DB를 분리시키는 선택을 하게 되었을 것 같습니다. 그러나 수집 데이터가 대부분의 용량을 차지하고 있었기에 DB 분리 말고 다르게 용량을 확보할 수 있는지 고민을 하게 되었습니다.
첫번째 살펴 본 부분은 로우 데이터 성격인 승인/매입 데이터였습니다. 서비스에서 사용하기 위해 생성한 거래 데이터가 있음에도 이 데이터들이 필요한 이유가 무엇일까 궁금했습니다. 이 데이터들의 필요성을 다른 방식으로 채워주게 되면 20TB 용량을 확보하게 될 수 있었기 때문입니다. 이 후 DB 분리를 진행할 때의 부담도 줄일 수 있을 것 같았습니다. 또한 이 데이터는 데이터 플랫폼팀에서 별도로 관리하고 있는 데이터라 캐시노트의 DB에서 삭제를 하더라도 필요 시 조회가 가능한 부분이었습니다.
승인/매입 데이터가 사용되고 있는 부분을 살펴보니 최초 거래 데이터를 설계했을 때의 기능에서 벗어나는 추가 데이터 및 조건이 생기게 되며 이 부분을 승인/매입 데이터와의 join을 통해 해결한 것이 아닐까 하는 부분들이었습니다.
두번째로 살펴 본 부분은 오래된 데이터의 클랜징이었습니다. 서비스 운영에 필요한 데이터는 2년까지로 정해지며, 오래된 데이터의 클랜징이 가능해졌습니다. 이부분을 제거하는 것도 유의미한 용량 확보가 가능할 것 같았습니다.
이 2가지 작업을 완료하게 되면 DB 분리 작업을 급하게 진행하지 않을 수 있을 것 같았습니다. 먼저 시간을 벌고, 로직 개선을 통해 다른 부분의 문제를 해결하는 것이 가능할 것 같았습니다. 그래서우선적으로 진행을 해보려 진행 가능 여부를 DBA에게 확인해보았습니다.
하지만 DB 파라미터 설정 등의 이슈와 Vaccum 등의 이슈, 그리고 높은 CPU 수치를 가진 현재 DB에 컬럼이나 인덱스 추가 등의 조치를 취하는 것이 어려웠습니다. 클랜징 또한 Vacuum 이슈로 당장 적용이 어려운 상황이라 적용이 어려운 상황이었습니다.
카드 매출 데이터 분리
우선적으로 캐시노트 전체가 중단 되는 것을 막고, 다른 작업들을 할 수 있는 기반을 만들기 위해서 대부분의 이슈를 가지고 있는 카드 매출 데이터의 DB를 분리시키기로 결정했습니다.
카드 매출 데이터 분리 시 위험이 될 수 있는 부분들을 먼저 점검을 해보았는데 아래와 같았습니다.
이 외에도 나중에 진행하며 알게 된 부분이지만, view나 function 등을 활용하고 있는 부분들이 많아 분리 전 처리에 놓치는 부분들이 존재했습니다.
코드 사전 조사
작업 방향을 결정하기에 앞서 어떤 방식으로 코드를 수정해야하는가를 확인하기 위해 조사를 시작했습니다.
쿼리 등이 도저히 손을 댈 수 없는 경우 로직 자체를 변경하여 개선작업을 먼저 진행해야 했기 때문에 가장 많은 기도가 필요했던 순간이었습니다.🙏
안타깝게도 처음 접하는 루비 온 레일즈라 코드를 탐색하는 것조차 서툴었지만, 그나마 친숙한 UI/UX를 가진 루비 마인을 통해서 조심스럽게 조사를 시작했습니다. 🔎
몇 번의 실험을 통해 루비 온 레일즈에서 사용하는 ActiveRecord 모델 class들의 usage만 찾는 것으로는 모든 사용처를 찾을 수 없다는 것을 확인했기에 두가지 단계로 나누어 조사를 시작했습니다.
불행인지 다행인지 모르겠지만, 캐시노트 코드에는 Business라고 하는 God object가 존재하고 있어 대부분의 로직은 이 객체를 통해 처리하고 있습니다. 핵심 기능의 경우 이 객체를 통해 호출되는 로직을 찾아 처리가 가능했습니다. 그래서 class usage 탐색으로 나오지 않는 부분들도 Business의 카드 매출 관련 프로퍼티를 텍스트로 탐색이 가능했습니다. 이 방식으로 대부분의 사용처를 조사할 수 있었습니다. (이렇게 해서 못 찾는 건 자주 안 쓰는 기능이기를 바라면서…🥲)
의외의 조사 결과
조사 결과를 정리하던 중 재미있었던 부분은 이관 대상으로 정해두었던 테이블 3개에 자주 join해서 사용하는 테이블 몇 개를 이관대상에 추가했더니, 타 테이블과의 연결이 거의 존재하지 않았습니다. 제가 조사하고도 신기해서 잘못 찾은 건가 3번 정도 더 확인을 해보았지만 정말 없는게 맞아서 놀라웠습니다. 🎉
>> Part. 2에서 계속