Post

공간 DB - PostGIS & Redis Geo

공간 DB - PostGIS & Redis Geo

들어가며

Real MySQL 8.0을 공부하면서 MySQL의 공간 검색 기능을 살펴봤다. ST_Distance_Sphere, ST_Contains, 공간 인덱스 등 MySQL도 나름 위치 기반 검색을 지원하지만, 찾아보니 위치 기반 서비스에 특화된 도구들 이 따로 있었다. 바로 Redis GeoPostGIS 다.

여러 블로그와 벤치마크를 찾아본 결과, 공간 검색 성능 면에서 PostGIS가 MySQL보다 빠르다는 글들이 많았고, 실시간 위치 추적에는 Redis Geo가 압도적이라는 이야기도 있었다. MySQL이 공간 검색 전문 도구는 아니다 보니, 위치 기반 서비스를 본격적으로 구현한다면 이 두 가지를 알아둘 필요가 있겠다 싶어 정리해본다.


왜 MySQL 말고 다른 걸 쓸까?

MySQL 공간 검색의 아쉬운 점들을 먼저 알아보자.

1. ST_Distance_Sphere가 인덱스를 타지 않는다.

반경 검색을 하려면 ST_Contains로 바운딩 박스를 먼저 필터링하고, 그 다음에 거리를 계산해야 한다. 쿼리가 복잡해지고, 실수하기 쉽다.

2. Geography 타입이 없다.

MySQL은 Geometry만 지원해서 지구 곡률을 고려한 거리 계산이 번거롭다. ST_Distance_Sphere 함수가 있긴 하지만, 이건 인덱스를 못 타니까 성능 문제가 생긴다.

3. 공간 함수가 제한적이다.

PostGIS가 300개 이상의 공간 함수를 제공하는 반면, MySQL은 상대적으로 적다. 복잡한 공간 연산이 필요할 때 한계가 있다.

실제 서울시 아파트 데이터(약 2,000여 건 범위 질의) 기준 벤치마크 결과를 보면:

방식 응답 시간
PostGIS ST_DWithin ~27.82ms
MySQL ST_Contains + ST_Distance ~136.16ms

영역 내 데이터를 추출하는 단순 선택(Selection) 질의에서 PostGIS가 약 5배 가량 빠르다. 특히 데이터 간의 관계를 계산하는 공간 조인(Natural Join) 연산으로 넘어가면 PostGIS(~267ms)가 MySQL(~9,699ms)보다 약 36배 이상 압도적인 성능을 보여준다.

물론 데이터 규모나 쿼리 복잡도에 따라 다르겠지만, 공간 검색과 복잡한 연산이 핵심인 서비스라면 고려해볼 만한 차이다.


PostGIS

PostGIS는 PostgreSQL의 확장으로, GIS 업계의 사실상 표준 이다. MySQL의 공간 기능이 “기본 탑재 내비게이션”이라면, PostGIS는 “전문 GIS 소프트웨어” 수준이다.

Geography 타입

PostGIS의 큰 장점 중 하나가 Geography 타입 이다. MySQL의 Geometry는 평면 좌표계라서 지구 곡률을 고려하지 않는다. 반면 PostGIS의 Geography는 지구를 구체로 보고 계산하기 때문에 거리가 자동으로 미터 단위 로 나온다.

1
2
3
4
5
6
7
8
9
10
-- Geography 타입으로 테이블 생성
CREATE TABLE places (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    location GEOGRAPHY(POINT, 4326)
);

-- 500m 이내 장소 검색 (거리 단위가 미터!)
SELECT name FROM places
WHERE ST_DWithin(location, ST_MakePoint(127.027, 37.498)::geography, 500);

ST_DWithin

MySQL에서 가장 아쉬웠던 점이 ST_Distance_Sphere가 인덱스를 타지 않는다는 것이었다.

PostGIS의 ST_DWithin 은 이 문제를 해결한다. “두 지점이 특정 거리 이내인가?”를 판단하면서 공간 인덱스(GiST)를 활용 한다.

중요: PostGIS에서도 ST_Distance를 WHERE 절에 쓰면 인덱스를 타지 않는다. 반경 검색에는 반드시 ST_DWithin을 써야 한다.

올바른 패턴은 이렇다:

1
2
3
4
5
-- ST_DWithin으로 필터링 (인덱스 사용) + ST_Distance로 정렬
SELECT name, ST_Distance(location, ST_MakePoint(127.027, 37.498)::geography) AS dist
FROM places
WHERE ST_DWithin(location, ST_MakePoint(127.027, 37.498)::geography, 1000)
ORDER BY dist;

폴리곤 연산

PostGIS의 진가는 복잡한 도형 연산 에서 나온다. Redis Geo로는 불가능한 것들이다.

1
2
3
4
5
6
7
8
-- 배달 구역(폴리곤) 안에 포함되는지 확인
SELECT name FROM delivery_zones
WHERE ST_Contains(area, ST_MakePoint(127.025, 37.495));

-- 두 배달 구역의 겹치는 영역
SELECT ST_Intersection(a.area, b.area)
FROM delivery_zones a, delivery_zones b
WHERE a.id = 1 AND b.id = 2;

배달 가능 구역 판정, 영역 겹침 계산, 면적 계산 같은 기능이 필요하다면 PostGIS가 답이다.


Redis Geo

Redis Geo는 Redis 3.2부터 추가된 기능이다. 이름만 들으면 뭔가 거창해 보이지만, 사실 Sorted Set에 Geohash를 score로 저장하는 방식 이다. 기존 Redis 자료구조를 영리하게 활용한 것이다.

동작 원리

위도와 경도를 입력하면, Redis는 이를 Geohash 라는 52비트 정수로 변환한다. 이 값이 Sorted Set의 score가 된다. Geohash는 가까운 위치일수록 비슷한 값을 가지도록 설계되어 있어서, Sorted Set의 범위 검색으로 근처 위치를 빠르게 찾을 수 있다.

1
2
3
4
5
# 위치 추가 (경도, 위도 순서 주의!)
GEOADD stores 127.027 37.498 "스타벅스강남"

# 반경 검색
GEOSEARCH stores FROMLONLAT 127.027 37.498 BYRADIUS 1 km WITHDIST

메모리 기반이라 속도가 압도적이다. 벤치마크에 따르면 초당 7만 건 이상 의 반경 검색을 처리할 수 있다.

언제 쓰면 좋을까

Redis Geo는 실시간 위치 추적 에 최적화되어 있다. 배달 라이더 위치처럼 초당 수백 번 업데이트되는 데이터를 다루기에 적합하다. 위치를 업데이트해도 트랜잭션이나 커밋 없이 즉시 반영되고, 바로 검색할 수 있다.

반면 Point만 지원 한다는 한계가 있다. Polygon(배달 가능 구역)이나 LineString(경로) 같은 복잡한 도형은 저장할 수 없다. “이 좌표가 배달 가능 구역 안에 있나요?” 같은 질문에는 답할 수 없다.

또한 메모리 기반이라 영속성 설정 을 신경 써야 한다. AOF나 RDB 설정 없이 서버가 재시작되면 데이터가 날아간다.

Redis Geo는 경도, 위도 순서다. 일반적으로 쓰는 위도, 경도 순서와 반대이므로 주의하자.


그래서 뭘 써야 할까

정리하면 이렇다.

Redis Geo를 쓰면 좋은 경우:

  • 실시간 위치 추적 (배달 라이더, 택시 등)
  • 초당 수백~수천 건의 위치 업데이트
  • 단순 반경 검색만 필요
  • 지연시간이 1ms 이하로 중요

PostGIS를 쓰면 좋은 경우:

  • 배달 가능 구역 같은 폴리곤 연산
  • 복잡한 공간 분석 (경로, 면적, 교집합 등)
  • 트랜잭션이 필요한 경우
  • 이미 PostgreSQL을 쓰고 있는 경우

MySQL 공간 검색으로 충분한 경우:

  • 데이터가 많지 않고 쿼리가 단순
  • 이미 MySQL을 쓰고 있고, 공간 검색이 핵심 기능이 아닌 경우
  • 별도 인프라를 추가하고 싶지 않은 경우

실제로는 Redis + PostgreSQL 조합 도 많이 쓴다. 실시간 위치(라이더)는 Redis에, 정적 데이터(가게, 배달 구역)는 PostgreSQL에 저장하는 식이다.


마치며

MySQL 공간 검색을 공부하다 보니 자연스럽게 “더 좋은 도구가 있지 않을까?” 하는 생각이 들었다. 찾아보니 역시 있었다.

PostGIS 는 기능에 올인한 도구다. 300개 이상의 공간 함수, Geography 타입, 인덱스를 타는 ST_DWithin 등 MySQL의 아쉬운 점들을 대부분 해결해준다.

Redis Geo 는 속도에 올인한 도구다. 메모리 기반으로 압도적으로 빠르지만, Point만 지원하고 영속성을 신경 써야 한다.

물론 MySQL 공간 검색이 쓸모없는 건 아니다. 이미 MySQL을 쓰고 있고, 공간 검색이 서비스의 핵심이 아니라면 MySQL로도 충분할 수 있다. 하지만 위치 기반 서비스를 본격적으로 구현한다면, Redis Geo나 PostGIS를 검토해볼 가치가 있다.


References

This post is licensed under CC BY 4.0 by the author.