선박 운항 데이터 수집 시스템
선박의 위치·방향 정보와 화재 센서 데이터를 실시간으로 수신·처리하는 시스템
아키텍처
개요
- 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건으로 바꿔 각각 검증
동일 부하 전후 결과
| 지표 | 단건 Insert | Batch Insert |
|---|---|---|
| TCP 입력 부하 | 600 messages/s | 600 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 소요시간 p95 | 35ms | 70ms | 110ms |
| 서버 평균 I/O wait | 약 7% | 약 5% | 약 3% |