판교역 기준 1·5·10·30km 주변 응급실을 찾는 두 쿼리를 비교했다. 두 경로 모두 거리를 계산하고 거리순으로 정렬한다. 정렬 없는 버퍼 쿼리의 이전 성능 수치는 이 비교에 포함하지 않았다.
A — MBR + 거리: SPATIAL 인덱스의 MBR 후보 검색 → ST_Distance_Sphere 구면 거리 계산 → 지정 반경 필터 → 거리순 정렬.
B — 버퍼 + 정렬: ST_Buffer 도형에 ST_Within 포함 판정 → ST_Distance_Sphere 거리 계산 → 거리순 정렬.
coordinate POINT SRID 4326 NOT NULL, SPATIAL INDEX idx_emergency_coordinate (coordinate).POINT(?, ?)의 JDBC 바인딩은 경도→위도. CSV 좌표와 경계 폴리곤 WKT에는 axis-order=long-lat 지정. MySQL SRID 4326의 기본 축 순서는 위도→경도지만, ST_SRID()는 이미 생성한 점의 좌표를 변환하지 않고 SRID만 바꾼다. MySQL 축 순서 · ST_SRID.초기 SQL은 거리식을 SELECT와 WHERE에 각각 적었다. SQL 표기만으로 실제 함수 평가 횟수를 확정할 수는 없다.
SELECT id, name,
ST_Distance_Sphere(coordinate, ST_SRID(POINT(?, ?), 4326)) AS distance_meters
FROM emergency FORCE INDEX (idx_emergency_coordinate)
WHERE active = TRUE
AND MBRContains(ST_GeomFromText(?, 4326, 'axis-order=long-lat'), coordinate)
AND ST_Distance_Sphere(coordinate, ST_SRID(POINT(?, ?), 4326)) <= ?
ORDER BY distance_meters;
변경 후에는 SELECT의 거리 별칭을 HAVING에서 사용했다. 이 별칭 사용법은 MySQL 문법에 의존한다.
SELECT id, name,
ST_Distance_Sphere(coordinate, ST_SRID(POINT(?, ?), 4326)) AS distance_meters
FROM emergency FORCE INDEX (idx_emergency_coordinate)
WHERE active = TRUE
AND MBRContains(ST_GeomFromText(?, 4326, 'axis-order=long-lat'), coordinate)
HAVING distance_meters <= ?
ORDER BY distance_meters;
두 경로 모두 거리순 정렬을 수행했다. 아래 값은 2회 평균이며, 변경 후 처리량과 p95가 개선됐다. 측정 시점이 달라 표현식 평가 횟수만의 효과로 단정하지 않는다.
| 반경 | 초기 RPS | 초기 p95(ms) | 변경 RPS | 변경 p95(ms) |
|---|---|---|---|---|
| 1km | 1399.55 | 185.26 | 1515.39 | 170.35 |
| 5km | 1192.28 | 172.86 | 1257.21 | 171.24 |
| 10km | 947.44 | 207.53 | 1038.65 | 188.46 |
| 30km | 146.66 | 1259.28 | 174.78 | 1026.40 |
RPS는 초당 요청 수, p95는 95백분위 응답 시간(ms). 모든 케이스 실패율 0%.