WORK EXPERIENCE

선박 운항 데이터 수집 시스템

선박의 위치와 방향 정보, 화재 센서 데이터를 실시간으로 수신하고 처리하는 시스템

Java, Netty, MySQL

아키텍처

선박 위치선박 방향화재 센서Ethernet ConverterSerial -> TCPNetty비동기 I/O 처리센서 메시지 분리BufferBatch InsertMySQL

개요

  • 군 보안 규정상 내부망을 거치지 않고 운항 데이터를 제공하는 외부 업체 서버와 직접 연결
  • 외부 업체 서버가 시리얼로 내보내는 신호를 Ethernet Converter가 TCP 소켓으로 전달
  • Netty 기반 비동기 TCP 서버에서 LineBasedFrameDecoder로 연속 바이트 스트림을 개별 센서 메시지로 디코딩
  • 파싱한 센서 값은 WebSocket으로 프론트에 즉시 전달하거나 내부 로직에서 사용하고 수신 원본은 로그 테이블에 적재

문제

  • 피크 시 초당 약 600건의 운항 데이터와 화재 센서 데이터를 기존 서비스 트래픽과 함께 처리
  • HDD 기반 스토리지의 낮은 IOPS(초당 처리 가능한 입출력 횟수)로 Write 처리량 제약
  • 초당 약 600건씩 로그 테이블에 단건 Insert하면서 서버 평균 I/O wait가 18%까지 상승

해결 전략

  • 실시간 전달과 내부 로직은 그대로 두고 로그 저장 경로만 버퍼를 거치도록 분리
  • 로그 데이터는 ConcurrentLinkedQueue에 쌓고, 600건이 모이거나 1초가 지나면 Batch Insert로 저장

기술 선택 이유

  • 단건 Insert - 제외

    • 커밋마다 Redo Log를 디스크에 동기화해야 하므로 초당 커밋 수가 HDD의 IOPS에 제한됨
  • Batch Insert - 채택

    • 600건을 한 트랜잭션으로 묶어 Redo Log 동기화를 1회로 감소
  • BlockingQueue, synchronized List - 제외

    • 저장 스레드가 락을 쥐는 동안 수신 스레드가 큐 앞에서 대기해 TCP 수신까지 지연
  • ConcurrentLinkedQueue - 채택

    • 락 없이 CAS로 동작해 수신 스레드가 큐에 넣고 바로 반환

검증

  • 선원 300명의 위치 측위 API 요청은 Locust로, 운항 데이터와 화재 센서 데이터는 TCP 시뮬레이터로 재현
  • 운영 피크인 600 messages/s를 5분간 유지해 단건 Insert와 Batch Insert 비교

동일 부하 전후 결과

지표단건 InsertBatch Insert
TCP 입력 부하600 messages/s600 messages/s
DB 저장 트랜잭션약 600회/s약 1회/s
서버 평균 I/O wait약 18%약 3%

회고

  • 피크 시 초당 약 600건은 센서 개수와 데이터 전송 주기를 고려해 가정한 값임. 평상시 유입량은 훨씬 적고 실제 분포는 측정하지 않음
  • flush 크기 600건과 주기 1초는 유입량을 기준으로 처리에 무리가 없을 것이라 예상해 정한 값임. 크기와 주기를 달리한 비교 테스트는 진행하지 못함
  • 다시 진행한다면 부하 기준을 세운 뒤 flush 크기와 주기를 바꿔가며 처리량, I/O wait, 데이터 대기 시간을 함께 측정해 운영 환경에 맞는 값을 찾아야 함