•
근접성 서비스는 음식점, 호텔, 극장 등 장소를 기반으로 현재 위치에서 가장 가까운 시설을 찾는데 사용된다.
◦
옐프 앱의 경우, 주변에 있는 좋은 식당 검색, 구글 맵의 경우 가까운 k개의 주유소 검색 등에 사용된다.
1단계: 문제 이해 및 설계 범위 확정
•
역시, 요구사항을 명확히 이해하고 설계 범위를 확정짓는 것이 가장 먼저다.
◦
사용자가 검색 반경을 지정하는가? 검색 반경 내에 조회된 장소가 미달된다면 시스템이 검색 반경을 알아서 넓혀야 하는가?
◦
최대 허용 반경은 얼마인가?
◦
사용자가 UI에서 검색 반경을 변경할 수 있어야 하는가?
◦
사업장 정보는 어떻게 관리되며 조회 시 실시간 성을 보장해야하는가?
◦
이동 중에는 시간 흐름에 따라 조회 데이터가 달라질 텐데, 화면을 자동 갱신해야 하는가?
기능 요구사항
•
위 질문들을 토대로 다음, 세 가지 핵심 기능을 추려내볼 수 있다.
◦
사용자의 위치와 검색 반경 정보에 매칭되는 사업장 목록을 반환한다.
◦
사업장 소유주가 사업장 정보를 관리할 수 있도록 하되, 그 정보가 검색 결과에 실시간으로 반영될 필요는 없다.
◦
고객은 사업자의 상세 정보를 조회할 수 있어야 한다.
비기능 요구사항
•
비기능 요구사항은 다음 정도로 추려진다.
◦
낮은 응답 지연: 사용자는 주변 사업장을 신속히 검색할 수 있어야 한다.
◦
데이터 보호: 사용자 위치는 민감한 정보이므로 위치 기반 서비스를 설계할 때는 항상 이 데이터를 보호할 방법을 찾아야한다.
▪
GDPR, CCPA 같은 데이터 사생활 보호 법안을 준수해야 한다.
◦
고가용성 및 규모 확장성: 인구 밀집 지역에서 트래픽이 느는 경우, 감당할 수 있도록 시스템을 설계해야 한다.
개략적 규모 추정
•
DAU가 1억 명이며 등록된 사업장은 2억이라고 가정해보자.
2단계: 개략적 설계안 제시 및 동의 구하기
API 설계
•
GET /v1/search/nearby
◦
이 API는 특정 검색 기준에 맞는 사업장 목록을 반환한다.
▪
보통 조회의 경우, 페이지네이션을 제공하므로 사용하지 않더라도 한 번 언급하면 좋을 것이다.
•
사업장 관련 API
◦
GET /v1/businesses/:id
◦
POST /v1/businesses
◦
PUT /v1/businesses/:id
◦
DELETE /v1/businesses/:id
데이터 모델
•
읽기/쓰기 비율
◦
읽기 요청이 더 많을 것이다. 이 경우, MySQL과 같은 관계형 데이터베이스가 바람직할 수 있다.
•
데이터 스키마
◦
business 테이블
◦
지리적 위치 색인 테이블
▪
이 경우, 위치 정보 관련 연산의 효율성을 높이는데 사용되며 해당 기술을 사용하기 위한 지오해시에 대한 지식이 필요하다.
개략적 설계
•
로드밸런서
•
위치 기반 서비스
◦
시스템의 핵심 부분으로, 주어진 위치와 반경 정보를 이용해 주변 사업장을 검색하는 서비스다.
◦
다음과 같은 특징을 갖는다.
▪
쓰기 요청이 없으며 읽기 요청만 발생한다.
▪
QPS가 높다.
▪
무상태 서비스이므로 수평적 규모 확장이 유리하다.
•
사업장 서비스
•
데이터베이스 클러스터
◦
주-부 데이터베이스 형태로 구성하여, 주 데이터베이스에는 쓰기 요청을, 부 데이터베이스에는 읽기 요청을 전담시킨다.
◦
데이터는 주에 먼저 기록된 다음 사본으로 복사되는데, 이때 복제에 걸리는 시간 지연 때문에 주-부 간의 데이터 차이가 있을 수 있다.
•
사업장 서비스와 LBS의 규모 확장성
◦
무상태 서비스이므로 수평적 확장을 이용해 트래픽에 대응할 수 있다.
◦
시스템을 클라우드에 둔다면 여러 리전에 두어 가용성을 더 확보해볼 수 있다.
주변 사업장 검색 알고리즘
•
많은 회사가 레디스 지오해시나 PostGIS 확장을 설치한 Postgres 데이터베이스를 활용한다.
•
이런 데이터베이스들을 알고 있는 것도 좋지만 몇 가지 방안을 훑어보고 그 이면의 사고 프로세스를 검토한 다음, 각 방안에 어떤 상충 관계가 존재하는지 논의해보자.
◦
방안 1: 2차원 검색
▪
주어진 반경, 원 안의 사업장을 검색하는 방법이다. 가장 직관적이지만 지나치게 단순하다.
▪
보통 성능 향상을 위해 데이터 색인을 도입할 것이다. 그러나 이 방안의 문제는 데이터베이스 색인으론 오직 한 차원의 검색 속도만 개선할 수 있다는 것이다.
▪
지리적 정보에 색인을 거는 방법
•
해시 기반 : 균등 격자, 지오해시, 카르테시안 계층 등
•
트리 기반 : 쿼드트리, 구글 S2, R 트리 등
◦
방안 2: 균등 격자
▪
지도를 작은 격자 또는 구획으로 나누는 단순한 접근법이다.
▪
사업장 분포가 균등하지 않아 이슈가 생길 수 있다. 이상적으로는 인구밀집 지역에는 작은 격자, 그렇지 않은 지역에는 큰 격자를 사용할 수 있다면 좋을 것이다.
◦
방안 3: 지오해시
▪
지오해시는 2차원의 위도, 경도 데이터를 1차원의 문자열로 변환한다.
▪
하나의 지도를 원하는 정밀도를 얻을 때까지 재귀적으로 4분할하여 대응시키는 것이다.
▪
이 접근법은 대체로 잘 동작하지만 격자 가장자리 처리 방식에 관한 경계 조건이 몇 가지 있다.
•
지오해시는 해시값의 공통 접두어가 긴 격자들이 서로 더 가깝게 놓이도록 보장하는데, 공통 접두어를 가졌음에도 먼 경우가 생길 수 있다.
⇒ 이 문제점 때문에 단순한 접두어 기반 SQL 질의문은 주변 모든 사업장을 가져온다고 보장할 수 없다.
•
두 지점이 공통 접두어의 길이는 같지만 서로 다른 격자에 놓이는 경우다.
•
표시할 사업장을 충분히 발견할 수 없는 경우
⇒ 주어진 반경 내 사업장만 반환하거나 검색 반경을 키워야 한다. 주어진 지오해시 값의 접두어를 하나씩 제거해가며 반경을 키우면 된다.
◦
방안 4: 쿼드트리
▪
쿼드트리는 격자의 내용이 특정 기준을 만족할 때까지 2차원 공간을 재귀적으로 사분면 분할하는데 흔히 사용되는 자료 구조다.
◦
방안 5: 구글 S2
▪
구글 S2 기하 라이브러리는 쿼드 트리와 마찬가지로 메모리 기반 솔루션이다.
▪
지구를 힐베르트 곡선이라는 공간 채움 곡선을 사용해 1차원 색인화하는 방안으로 힐베르트 곡선의 특성 중 하나인 곡선 상 인접한 두 점은 1차원 공간 내에서도 인접하다는 것을 사용한다.
▪
지오펜스 구현에 유용한데, 임의 지역에 다양한 수준의 영역 지정이 가능해서다. 또 한가지 장점은 영역 지정 알고리즘을 유연하게 제공할 수 있다.
◦
추천
색인 방법 | 회사 |
지오해시 | 빙, 레디스, 몽고DB, 리프트 |
쿼드트리 | 엑스트 |
지오해시 + 쿼드트리 | 엘라스틱서치 |
S2 | 구글 맵, 틴더 |
▪
면접 시에는 쿼드 트리나 지오해시 중 하나를 선택하길 추천한다. S2는 면접 시간 안에 설명을 끝마치기에 까다롭기 때문이다.
지오해시 vs 쿼드트리
•
지오해시
◦
구현과 사용이 쉽다. 트리를 구축할 필요가 없다.
◦
지정 반경 이내 사업장 검색을 지원한다.
◦
정밀도를 고정하면 격자 크기도 고정된다. 인구 밀도에 따라 동적으로 조정이 불가하며 가능케하려면 복잡한 논리를 적용해야 한다.
◦
색인 갱신이 쉽다.
•
쿼드트리
◦
구현하기가 살짝 더 까다롭다. 트리 구축이 필요하기 때문이다.
◦
k 번째로 가까운 사업장까지의 목록을 구할 수 있다.
◦
인구 밀도에 따라 격자 크기를 동적으로 조정할 수 있다.
◦
색인 갱신이 까다롭다. 트리 형태이므로 추가, 삭제 등 조작이 발생하는 경우, 조정이 필요하다.
3단계: 상세 설계
데이터베이스의 규모 확장성
•
사업장
◦
한 서버에 담을 수 없을 수 있으므로 샤딩을 적용하기 좋다.
◦
사업장 ID를 기준으로 샤딩하여 모든 샤드에 부하를 고르게 분산할 수 있다.
•
지리 정보 색인
◦
좀 더 단순한 지오해시를 기준으로 구성한다면 다음 두 가지 방안이 존재한다.
1.
각각의 지오해시에 연결되는 모든 사업장 ID를 JSON 배열로 만들어 같은 열에 저장한다.
2.
같은 지오해시에 속한 사업장 ID 각각을 별도 열로 저장한다.
◦
두 번째 방법을 추천하는데, 그 이유는 사업장 정보 갱신시 JSON 배열을 찾아내 갱신할 사업장 ID를 찾아야하므로 모든 데이터를 살펴봐야 한다.
◦
2의 경우, 이때 지오해시와 사업장 ID 칼럼을 합친 복합 인덱스를 사용하면 사업장 정보 관리가 쉽다.
•
지리 정보 색인의 규모 확장
◦
데이터가 그리 크지 않기에 저장 자체는 하나의 데이터베이스에서도 충분히 수행할 수 있으나 QPS가 높으므로 읽기 연산을 지원하는 사본을 도입할 수 있다.
캐시
•
캐시 계층 도입 전에는 항상 정말 필요한가?라는 질문을 날려야 한다.
•
처리 부하가 읽기 중심이고 데이터베이스 크기는 상대적으로 작아서 하나의 데이터베이스 서버가 수용하므로 가급적 모든 데이터를 메모리에 올려놓고 처리할 가능성이 있다.
•
읽기 성능이 병목이라면 사본 데이터베이스를 늘려 읽기 옵션을 추가해줄 수 있다.
•
지오해시의 경우, 지오해시 값을 캐시 키로 사용하여 캐시 계층을 도입할 수 있다.
지역 및 가용성 구역
•
사용자와 시스템 간의 물리적 거리를 줄이기 위해 도입해볼 수 있다.
시간대, 혹은 사업장 유형별 검색
•
사업장 ID부터 확보한 후, 사업장 내부 정보를 토대로 필터링을 수행하면 얻을 수 있다.
4단계: 마무리
•
지리 정보 색인 기법을 사용하는 전형적인 LBS 서비스를 설계했다. 이번 장에서는 다음 몇 가지 색인 방안을 살펴보았다.
◦
2차원 검색
◦
균등 분할 격자
◦
지오해시
◦
쿼드트리
◦
구글S2
1장 요약
•
근접성 서비스
◦
1단계
▪
기능적 요구사항
•
주변 사업장 검색
•
사업장 정보 관리
•
사업장 정보 조회
▪
비기능적 요구사항
•
낮은 응답지 연
•
데이터 보호
•
5k 검색 QPS
◦
2단계
▪
API 설계
•
검색 API
•
사업장 정보 관리 API
•
페이지 분할
▪
데이터 모델
•
읽기/쓰기 연산 비율
•
데이터 스키마
▪
개략적인 설계
▪
알고리즘
•
2차원 검색
•
균등 분할 격자
•
지오해시
•
쿼드트리
•
구글 S2
•
지오해시 vs 쿼드트리
◦
3단계
▪
데이터베이스 규모 확장
•
사업장
•
지리 정보 색인
▪
캐시
•
캐시 키
•
데이터 유형
▪
지역 및 가용성 구역
▪
결과 필터링
▪
최종 설계
◦
4단계
▪
마무리