Skip to Content
← 블로그
개선기

로그 저장 구조를 바꾸고 인덱스를 적용하기까지

개선 진행 기록 — 운영 적용과 결과 측정은 준비 중

2026-09-30 개정. 운영 현황 조사와 인덱스 후보 검토는 1편에 정리했습니다. 이 글은 모듈 분리·팀 설득·개선 진행과 결과를 이어 기록합니다. 현재는 조사와 분리 방향을 정리한 단계이며, 운영 적용 결과는 아직 없습니다.

  • 배치와 실시간 처리의 배포 단위를 나누고, 로그 저장을 Kafka 소비자로 옮기는 방향을 잡았다.
  • 인덱스 작업 중에는 로그 소비를 멈추고, 작업 후 적재 속도를 제한해 밀린 로그를 처리할 계획이다.
  • 실제 배포·DDL 소요시간·조회 개선·승인 영향은 적용 후 측정해 이 글에 추가한다.

지금 끝난 것과 남은 것을 구분했다

운영 로그 테이블의 보조 인덱스 부재와 전체 스캔 계획을 확인했고, 비동기 로그 저장과 배치 배포의 영향을 조사했다. 팀에 설명할 자료에는 분리 이유뿐 아니라 같은 서버·DB를 계속 사용하는 한계와 운영 대상이 늘어나는 비용도 담았다.

여기까지가 현재 확보한 기록이다. 최종 인덱스 구성, 발행 실패 정책, 중복 처리 방식과 상세 이관 절차는 구체화해야 한다. 팀 전체의 최종 동의나 배포가 끝났다는 뜻은 아니다.

비동기 로그 저장에도 호출자가 기다리는 경로가 있었다

로그 저장은 @Async였지만 공용 executor를 사용했다. 설정은 core 4, max 8, 큐 200, 거부 정책은 CallerRunsPolicy였다. 작업자와 큐가 포화되면 호출한 스레드가 저장 작업을 직접 실행할 수 있다. Java CallerRunsPolicy

전용 로그 executor도 정의돼 있었지만 같은 거부 정책을 썼다. 이름만 바꾸면 공용 풀 경쟁은 줄일 수 있어도 호출자 실행과 공유 DB 연결의 문제를 모두 없앨 수는 없었다.

테이블당 10분 정도를 작업 예상으로 잡았지만, 그 시간이 곧 쓰기 중단 시간은 아니다. MySQL의 온라인 보조 인덱스 생성은 동시 DML을 지원하며 메타데이터 락 대기 등은 별도로 남는다. 작업시간과 승인 영향을 실제로 확인해야 한다. 온라인 DDL, 제약 사항

배포 때문에 실시간 처리가 늦어지는 문제도 겹쳤다

한 연동사에서는 거래 완료와 실시간 데이터 수신 사이에 3분 이상 차이가 난다는 문의가 있었다. 운영에서는 잦은 배치 애플리케이션 배포를 원인으로 보고 있었다. 정확한 지연 구간은 배포·전송 시각을 대조해야 하지만, 배치와 실시간 리스너가 한 프로세스에 있어 함께 재기동되는 구조는 분명했다.

그래서 로그 소비자를 기존 배치 앱에 더 얹기보다, 실시간 처리와 로그 저장을 별도 앱으로 옮기는 방향을 잡았다.

선택이유와 남는 비용
같은 서버의 별도 프로세스배치와 실시간 처리를 따로 배포한다. 외부기관 연결 검증이 큰 인프라 이전은 후속으로 미룬다
Kafka를 거쳐 로그 저장소비 중단·재기동 뒤에도 브로커에 보관된 로그를 이어 처리한다. 발행 실패와 보관 용량은 별도 관리한다
구조 전환 후 인덱스 적용기존 직접 쓰기를 제거하고 로그 소비를 멈춘 뒤 작업한다. 재개할 때 적재 속도를 제한한다

같은 DB에 쓰므로 DB 부하가 사라지는 것은 아니다. 같은 서버의 자원도 공유한다. 팀원이 알아야 할 앱과 운영 대상은 하나 늘어난다. 기대 효과와 이 비용을 함께 의사결정 자료에 담았다.

먼저 로그 저장과 배치의 배포 경계를 바꾼다

실시간 처리 앱은 승인 요청·응답 로그 수집과 외부기관에 대한 후속 처리·전송을 맡는다. 배치 앱은 정해진 시점의 배치 처리를 맡는다. 같은 서버에 별도 프로세스로 배포할 계획이다.

현재

목표 구조 — 구현·적용 전

프로세스가 나뉘어도 공유 DB와 호스트의 자원 경쟁은 남는다. 신규 앱 최대 힙은 1GiB로 계획했지만, 힙이 전체 프로세스 메모리는 아니다. 실제 RSS·GC와 배치 실행 시간대의 자원 사용을 확인해야 한다.

인덱스 작업 중에는 로그 소비만 멈춘다

계획한 순서는 다음과 같다.

DDL이 진행되는 동안에는 로그 소비만 중단하도록 분리해야 한다. 다만 로그 소비를 재개하려고 앱 전체를 재배포하면 같은 프로세스의 다른 리스너도 일시 중단될 수 있다. 재기동 없이 로그 리스너만 재개할지, 재배포 중단 시간을 어떻게 관리할지는 상세 설계에서 정하고 검증할 항목이다.

DB 저장만 건너뛰면서 offset을 처리 완료로 기록해서도 안 된다. 그 상태에서는 재배포해도 밀린 로그를 자동으로 다시 읽지 못한다. 같은 consumer group의 진행 위치를 유지하고, DB 저장 성공 이후 offset을 커밋하는 순서를 검증해야 한다. 이 순서에서도 장애 시 재전달은 가능하므로 중복 처리가 필요하다. Kafka 전달 보장

브로커에 정상 발행된 로그가 작업 기간 동안 보관될 용량과 retention도 확보해야 한다. 저장 재개 후에는 소비량을 무작정 늘리지 않고 DB와 다른 업무가 감당하는 속도를 찾는다.

발행이 지연될 때의 동작도 정해야 한다

Kafka를 사용한다고 승인 스레드의 대기가 자동으로 사라지지는 않는다. send()는 비동기 발행 API지만 메타데이터 확보나 버퍼 공간 대기에서 막힐 수 있다. max.block.ms가 직렬화 등 호출 경로 전체 시간을 제한하는 값도 아니다. Kafka Producer 설정

로그 전용 executor에서 발행할지, 호출 스레드에서 발행할지 비교할 예정이다. 결정할 것은 다음 세 가지다.

  • 승인 처리에서 허용할 발행 대기시간과 직렬화 비용
  • 큐·브로커가 포화되거나 발행이 실패했을 때의 처리와 관측 방법
  • 진단 로그와 결제에 필수적인 후속 이벤트의 서로 다른 유실·재시도 정책

로그 전용 executor로 이름만 바꾸고 같은 CallerRunsPolicy를 두는 것으로는 호출자 대기를 끊을 수 없다. 전환 전 실패 경로까지 검증해야 한다.

적용 결과는 같은 조건으로 비교한다

8월 개발환경 기록에는 1,881ms → 0.108ms 개선이 남아 있다. 운영보다 작은 데이터에서 얻은 특정 조회 결과이므로 이번 운영 목표나 보장값으로 사용하지 않는다. 최종 비교에는 같은 SQL·조회값·기간·반환 컬럼을 사용하고, 실행 시점과 반복 조건을 함께 기록할 예정이다.

확인할 항목현재 근거적용 후 기록
인덱스 정의운영 네 테이블에 PK만 존재최종 컬럼 순서·정의와 생성 결과
대표 조회 계획9월 조사 8개 모두 ALL같은 SQL의 key·접근 방식·추정 행수
실제 조회시간8월 45.36초 기록, 9월 재측정 없음동일 조건의 적용 전후 측정
DDL 영향테이블당 10분은 예상치실제 작업시간·잠금 대기·DB 자원
승인 처리이번 작업 기준선 미측정지연·오류율의 작업 전후 변화
누적 로그 회복소비 중단·재개 계획lag·적재 속도·회복 시간·중복/누락
같은 서버의 배치별도 JVM 배포 계획RSS·GC·배치 실행시간 변화

EXPLAIN ANALYZE는 SELECT를 실제 실행한다. 운영에서 반복 측정할 때는 부하와 실행 한도를 먼저 정해야 한다. 인덱스가 선택됐다는 사실과 충분히 빨라졌다는 사실도 따로 확인한다. MySQL EXPLAIN ANALYZE

이 글의 마지막에 남길 결과는 ‘인덱스를 만들었다’뿐이 아니다. 작업 중 승인은 어땠는지, 로그를 얼마나 쌓았다가 얼마 만에 처리했는지, 배치와 실시간 처리를 독립적으로 배포할 수 있게 됐는지까지 확인하려 한다. 실제 적용 기록이 생기면 이 표와 함께 보완하겠다.