DreamiOfflineDetector.detectOfflineDreamis()delivery.dreami-offline-scan-interval=5s 주기로

DeliveryRepository.findStaleLocationDeliveries()를 호출한다.

@Query("SELECT d FROM Delivery d WHERE d.deliveryCd IN :statuses "
        + "AND d.lastLocationDtm IS NOT NULL AND d.lastLocationDtm < :threshold")

그런데 sql/sym-boorm-ddl.sql의 DELIVERY 테이블에 걸린 인덱스는 PK_DELIVERY(delivery_id)

IX_DELIVERY_ORDER_ID(order_id) 둘뿐이다. delivery_cd, last_location_dtm 어디에도 인덱스가 없으므로

이 쿼리는 5초마다 DELIVERY 풀스캔을 돈다. 진행중 배달 건수는 일정한데 배달 내역은 계속 쌓이기만 하므로,

테이블이 커질수록 스캔 비용은 선형으로 늘어나는데 실제로 필요한 행은 진행중인 소수뿐이다.

이 디렉터리는 아래 복합인덱스를 실제로 적용하기 전에 근거 숫자를 만들기 위한 것이다.

CREATE INDEX IX_DELIVERY_STATUS_LAST_LOCATION ON DELIVERY (delivery_cd, last_location_dtm);

delivery_cd를 선두에 두는 이유는 종료·취소된 과거 배달을 먼저 제외하기 위해서다.

그 배달들은 last_location_dtm이 계속 과거로 남아 있어 last_location_dtm < threshold를 그대로 통과한다.

last_location_dtm 선두 인덱스는 이들을 범위 안에 그대로 끌고 들어오지만, delivery_cd 선두 인덱스는

두 상태값에 대한 range 두 개로 진행중 배달만 먼저 잘라낸다.

동시에 위치가 수신될 때마다 last_location_dtm이 갱신되므로 이 인덱스는 쓰기 경로에서 매번 유지비를 낸다.

그래서 읽기 이득과 쓰기 비용을 같이 측정한다.

n-plus-1-nearby-test/는 서버를 띄우고 k6로 부하를 거는 방식이지만, 여기서는 서버도 k6도 쓰지 않고

MySQL 클라이언트만으로 SELECT / UPDATE 성능을 직접 잰다.

구성

파일 역할
docker-compose.yml 이 실험 전용 MySQL 8.0 (포트 3308). n-plus-1-nearby-test의 3307 컨테이너와 동시에 떠 있어도 충돌하지 않는다
seed.sql ORDERS 20,000건 + DELIVERY 20,000건 목 데이터. 재실행 가능
bench.sql 워밍업 + 4종 측정(스캔량 / 실행계획 / SELECT 반복 / UPDATE 반복). 전·후에 똑같이 실행한다
add-index.sql 복합인덱스 생성 + ANALYZE TABLE
drop-index.sql 다음 회차를 위한 롤백

측정 결과 원본은 results/<N>라운드/ 아래에 회차별로 담겨 있다.