WORK EXPERIENCE

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

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

Java · Netty · MySQL

아키텍처

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

개요

  • Ethernet Converter가 TCP 연결로 전달하는 선박의 위치·방향 정보와 화재 센서 데이터를 실시간 수신
  • Netty 기반 비동기 TCP 서버에서 연속 바이트 스트림을 줄바꿈 기준의 센서 메시지로 나눠 해석한 뒤 DB 저장

문제

  • 초당 약 600건의 운항·화재 센서 데이터가 추가되며 기존 선원 위치 요청과 함께 처리
  • HDD 기반 스토리지의 낮은 IOPS(초당 처리 가능한 입출력 횟수)로 Write 처리량 제약
  • 단건 Insert로 DB 트랜잭션이 약 600회/s 발생하고 서버 평균 I/O wait가 18%까지 상승

해결 전략

  • 수집 데이터는 ConcurrentLinkedQueue에 쌓고, 600건이 모이거나 1초가 지나면 Batch Insert로 저장
  • 운항 데이터와 화재 센서 평상값은 다음 수신값으로 최신 상태를 갱신할 수 있어 일부 유실 허용
  • 실제 화재 이벤트는 버퍼를 우회해 전용 테이블에 즉시 저장

기술 선택 이유

  • Batch Insert

    • 단건 Insert 때마다 발생하던 DB 왕복과 커밋을 600건 단위로 줄여 I/O 부담 완화
  • ConcurrentLinkedQueue

    • 수집 스레드와 배치 저장 스레드 간 Lock 경합을 줄이고 수신과 DB 저장을 비동기로 분리

튜닝 기준

  • Flush 크기: 100·300·600건 중 I/O wait가 가장 낮은 600건 선택
  • Flush 주기: 600건이 모이지 않는 구간에도 데이터 대기시간이 1초를 넘지 않도록 설정

검증

테스트 설정

  • Locust로 선원 300명의 측위 API 요청 재현
  • NMEA 형식의 운항·화재 센서 데이터는 별도 TCP 시뮬레이터로 재생
  • 운영 피크인 600 messages/s를 10분간 유지해 단건 Insert와 Batch Insert 비교
  • Flush 주기는 1초로 고정하고 크기만 100·300·600건으로 바꿔 각각 검증

동일 부하 전후 결과

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

Flush 크기 비교 결과

지표100건300건600건
DB 저장 트랜잭션약 6회/s약 2회/s약 1회/s
flush 소요시간 p9535ms70ms110ms
서버 평균 I/O wait약 7%약 5%약 3%