![[Spring] 놀멍 서비스 개발 일지 - 지도 화면 개발하기2(공간 인덱스 적용)](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpUEC2%2FbtsLEYP05zm%2FpvfNw2gPZJ05KAotXOSOQK%2Fimg.jpg)
이 글은 반려견 동반 가능 시설 공유 플랫폼 '놀멍'의 메인 화면인 지도를 개발하는 과정입니다.
놀멍 서비스 개발 일지 - 지도 화면 개발하기1 이전 글에서 지도 화면을 개발한 내용을 공유했습니다.
'놀멍' 서비스의 지도를 완성하였고, 실제 서비스 배포를 하였는데요!
그리고 운영을 하면서 개발을 진행하던 도중 우연히 우테코 5기 폴로님의 글을 읽고 공간 인덱스를 알게 되었습니다.
마침 개발 중인 지도 기능에도 공간 인덱스를 적용해 볼 수 있을 것 같아, 그 과정을 공유하고자 합니다.
사실, 현재 지도 화면에서 장소를 조회하는 데 성능적인 큰 문제는 없었지만...그냥 해보고 싶었습니다.ㅇㅁㅇ
공간 인덱스(Spatial Index)란?
공간 인덱스는 공간 데이터를 효율적으로 저장하고 검색하기 위한 데이터 구조입니다.
공간 데이터는 주로 GIS(Geographic Informatioin System)에서 사용되며, 지도상의 점, 선, 면 등의 형태로 존재합니다.
공간 인덱스의 구조로는 R-Tree, Quad-Tree, KD-Tree 등이 있습니다.
이 중에서 저는 R-Tree 구조를 사용했습니다.
그 이유는 R-Tree가 MySQL 및 PostGIS를 포함한 GIS 데이터베이스에서 널리 사용되는 데이터 중심 구조이며, 관련 레퍼런스도 많았기 때문입니다.
R-Tree의 검색 성능은 평균적으로 O(logMn)입니다. 여기서 M은 페이지의 최대 항목 개수를 의미합니다.
R-Tree는 공간 데이터의 MBR을 만들어 이를 노드로 관리하는 트리 구조입니다.
MBR은 Minimum Bounding Rectangle(최소 경계 사각형)의 약자로 공간 데이터의 경계를 정의하는 최소 경계 사각형으로, 특정 객체나 데이터 포인트를 포함하는 직사각형을 의미합니다.
위와 같이 지도에 위치한 Point(빨간 점), LineString(초록선), Polygon(보라색 다각형)이 있을 때, 각 공간 데이터의 MBR은 다음과 같습니다.
이렇게 만들어진 MBR들을 포함하는 더 큰 MBR을 만들고, 그러한 MBR들을 포함하는 또 다른 MBR을 만들고...
최종적으로 생성된 MBR들은 다음과 같은 형태로 나타납니다.
그리고 이를 트리 형태로 표현하면 아래와 같습니다.
R-Tree에서 데이터를 찾는 과정은 B-Tree와 유사합니다.
공간 인덱스 적용
CREATE SPATIAL INDEX geo_index ON place_position (location); #인덱스 생성
alter table place_position add spatial index geo_index (location); #인덱스 생성2
DROP INDEX geo_index ON place_position; #인덱스 삭제
SHOW INDEX FROM place_position; #인덱스 조회
공간 인덱스는 CREATE 또는 ALTER TABLE 명령어를 사용하여 생성할 수 있습니다.
인덱스 생성 후에는 SHOW INDEX FROM 명령어를 통해 인덱스가 제대로 생성되었는지 확인할 수 있습니다.
다음 단계로, Polygon을 생성했습니다.
Spring에서 위도와 경도를 기반으로 Polygon을 생성하는 코드는 앞 전 글에 있고, 아래 코드는 MySQL 콘솔 창에서 임의로 Polygon을 생성한 예제입니다.
SET @latitude = 37.57506947684322;
SET @longitude = 126.98615389041305;
SET @maxLatitude = 37.585174823156784;
SET @maxLongitude = 127.02125550958695;
SET @minLatitude = @latitude - (@maxLatitude - @latitude);
SET @minLongitude = @longitude - (@maxLongitude - @longitude);
SET @polygon = ST_SRID(ST_PolygonFromText(CONCAT(
'POLYGON((',
@maxLongitude, ' ', @maxLatitude, ', ',
@minLongitude, ' ', @maxLatitude, ', ',
@minLongitude, ' ', @minLatitude, ', ',
@maxLongitude, ' ', @minLatitude, ', ',
@maxLongitude, ' ', @maxLatitude,
'))'
)), 4326);
SELECT @polygon; # @polygon 변수 값 조회
SELECT ST_SRID(@polygon) as srid; # @polygon 변수의 SRID 값 조회
이때 주의할 점은 SRID 값을 반드시 맞춰야 한다는 것입니다. 저는 지도 화면을 만들 때, 장소 Point 타입의 데이터에 SRID 값을 4326으로 설정하였습니다. 그렇기에 MySQL 콘솔창에서 생성한 Polygon 또한 4326으로 SRID 값을 설정하였습니다.
조회 쿼리
explain
select p.*
from place_position pp
join place p on pp.place_id = p.place_id
where st_contains(@polygon, pp.location);
지난 글에서 언급한 것처럼, ST_CONTAINS 공간 함수를 사용하여 해당 Polygon 내에 있는 Point 타입의 공간 데이터를 조회합니다.
쿼리 실행 결과를 확인한 결과, geo_index가 잘 적용되어 있는 것을 확인할 수 있습니다.
주의!!!
1. SRID 값의 일치와 NOT NULL 조건
공간 인덱스를 적용하기 위해서는 SRID 값을 일치시켜야 합니다.
저는 Polygon과 Point 모두 4326으로 설정했습니다. 만약 SRID 값을 따로 설정하지 않으면 기본값인 0으로 설정됩니다.
그리고 공간 인덱스를 적용하는 컬럼은 반드시 NOT NULL 조건이 있어야 합니다.
2. 공간 함수와 인덱스 적용 가능 여부
ST_CONTAINS 함수는 공간 인덱스가 적용되는 함수 중 하나이지만, ST_DISTANCE와 같은 몇몇 함수들은 공간 인덱스가 적용되지 않습니다. ST_DISTANCE는 MBR들의 포함 관계를 확인하는 것이 아닌, 모든 데이터와 기준점 간의 거리를 계산하여 반환하는 쿼리이기 때문에 공간 인덱스가 적용되지 않습니다.
공간 인덱스 적용 결과
524개의 수도권 장소 데이터 조회 결과
속도가 128% 증가하였습니다. 하지만 이 결과만 놓고 보면 사용자 입장에서 큰 차이를 느끼기 어려울 것입니다.
고작 0.016초 차이이기 때문입니다.
하지만 저희 놀멍 서비스가 전국으로 더욱 확장됨을 고려하여, 전국 23,929개의 반려견 동반 가능 시설 데이터에서 조회를 수행했을 때의 결과는 다음과 같습니다.
여기서는 인덱스 적용 전보다 482%의 속도 증가한 유의미한 성능 개선 수치를 보여줍니다.
지금 데이터베이스에는 수도권 524개의 장소 데이터만 존재하기 때문에 큰 성능 차이는 느끼지 못하고 있지만, 데이터가 증가할 수록 성능 차이는 유의미하다는 것을 알 수 있었습니다.
네이버 지도나 카카오맵과 같은 경우에는 장소 데이터가 워낙 많기 때문에 공간 인덱스 적용 여부에 따라 성능 차이가 상당히 클 것 같습니다..!
참고자료
공간 데이터 개념부터 적용까지
…
tecoble.techcourse.co.kr
'Spring' 카테고리의 다른 글
[Spring] 놀멍 서비스 개발 일지 - 로그 시스템 구축하기2 (2) | 2025.02.04 |
---|---|
[Spring] 놀멍 서비스 개발 일지 - 로그 시스템 구축하기1 (3) | 2025.01.25 |
[Spring] 놀멍 서비스 개발 일지 - 지도 화면 개발하기1 (0) | 2025.01.05 |
[Spring] Redis 테스트 환경 구축하기(Embedded Redis) (2) | 2024.12.31 |
[Spring] URL 이미지 리사이징 후, S3에 업로드 (3) | 2024.07.28 |
느리더라도 단단하게 성장하고자 합니다!
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!