복잡한 레거시 주문 조회를 운영 모니터링에 계속 사용하면서 발생한 느린 검색과 장애를 Kafka·Elasticsearch·Kibana 기반 이벤트 파이프라인으로 분리한 사례다. PHP 애플리케이션이 주문 이벤트를 Kafka에 비동기로 발행하고 ELK 대시보드가 실시간·누적 통계를 제공한다. 첫 배포 뒤 발견한 flush, ack, 정합성, 중복 문제까지 함께 다룬다.
핵심 포인트- 기존 조회는 7개 이상 테이블 조인, 4개 이상 서브쿼리, GROUP BY·HAVING·집계가 얽혀 주문이 몰리면 검색과 DB가 함께 느려졌다.
- 주문 도메인 이벤트를 Kafka에 발행하고 Elasticsearch에 적재해 운영 조회 부하를 트랜잭션 DB에서 분리했다.
- 서버별 파일 로그와 Filebeat 방식은 디스크·권한·배포 중 유실 위험이 있어 Kafka의 전달·내결함성을 택했다.
- php-rdkafka의 produce는 비동기라 프로세스 종료 전에 flush하지 않으면 전송되지 않은 데이터가 유실될 수 있다.
- 배포 뒤 ack 설정, 누락 이벤트의 DB 정합성 보완, 로그 ID 기반 멱등 처리가 추가로 필요했다.
상세 정리- 운영 요구: 24시간 주문과 새벽 배송을 지원하려면 주문 상태·처리 과정·상품·금액 통계를 실시간으로 확인해야 했다.
- 레거시 증상: 주문량이 몰리면 주문서를 찾지 못하거나 시간 범위를 잘게 나눠 검색해야 했다. 데이터 정합성 문제로 특정 주문이 검색되지 않는 경우도 있었다.
- 쿼리 병목: 다수 조인과 중첩 서브쿼리 위에 sum, count, distinct, GROUP BY, HAVING을 한 목록 조회에서 처리했다. 응답을 기다리는 동안 타임아웃과 DB 부하가 커졌다.
- 코드 구조: 2천 줄이 넘는 비즈니스 로직과 뷰가 한 파일에 섞였고, 조건문으로 SQL 문자열을 조립해 튜닝과 변경이 어려웠다.
- 전환 결정: 주문 도메인에서 이벤트가 생길 때마다 Kafka로 데이터를 보내고 기존 사내 ELK 플랫폼에서 Kibana 대시보드를 만들기로 했다.
- 범위 절감: Kafka와 ELK 기반이 이미 있어 애플리케이션 연동, 이벤트 데이터 구조, 대시보드 구현에 집중할 수 있었다.
- 파일 로그 비교: 각 인스턴스 파일과 Filebeat 방식은 disk full, 쓰기 권한 오류, 배포 중 파일 유실 가능성이 있었다. 서버 로컬 상태에서 전송 채널을 분리할 필요가 있었다.
- 클라이언트: PHP 애플리케이션에는 php-rdkafka를 설치했다. Producer에 브로커를 등록하고 주문 payload를 Topic으로 produce했다.
- 비동기 함정: 처음에는 produce 호출 뒤 전송 완료 전에 PHP 프로세스가 끝나는 패턴을 놓쳐 원인을 찾기 어려운 유실이 생겼다.
- flush 적용: 미전송 메시지가 있으면 제한 시간 동안 flush해 전송을 마친 뒤 프로세스를 종료하도록 바꿨다.
- 대시보드 결과: 실시간·누적 주문 건수와 금액, 주문 상품 통계를 Kibana에서 조회했고 접속 후 체감 약 1초 안에 결과를 볼 수 있었다.
- 인프라 장애 식별: 첫 배포 약 일주일 뒤 특정 시간대 데이터가 사라졌고, 대시보드로 주문 시스템이 아니라 데이터 인프라 장애임을 확인했다.
- 정합성 과제: 이벤트 수집이 하나라도 누락되면 DB의 현재 상태와 Elasticsearch 데이터가 달라졌다. 실시간 파이프라인 외 보정 경로가 필요했다.
- 전달 보완: ack 옵션을 추가해 Kafka 전송 확인 수준을 명시했다. 속도와 허용 가능한 유실률에 맞춰 설정해야 한다.
- 중복 대응: 같은 데이터가 여러 번 발생할 가능성을 고려해 로그 ID를 지정하고 소비·저장 과정에서 멱등성을 확보하려 했다.
왜 읽나레거시 트랜잭션 DB의 무거운 운영 조회를 이벤트 스트림과 검색 대시보드로 분리할 때 생기는 전달 보장과 정합성 과제를 함께 볼 수 있다.