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>라운드/ 아래에 회차별로 담겨 있다.