[1권 - 13장] 검색어 자동완성 시스템 #23
Replies: 7 comments 17 replies
|
데이터가 많이 흐르는 시스템에서 실시간은 바람직하지 않다고 소개 되어있음 바람직하지 않은 이유와 바람직하지 않지만 실시간이 필요한 경우는 뭐가 있을까요? |
|
트라이 자료구조에서 검색어와 함께 빈도를 저장하는데, 이를 시스템 관점에서 본다면 어떻게 설계되며 어떤 기술을 활용할 수 있을까? |
|
질의당 바이트 단위로 계산하는 이유는 메모리 사용량, 네트워크 전송량, 저장소 크기 등을 예측하기 위함으로 알고 있는데, 실제 현업에서 바이트 기준으로 설계 계획을 한 적이 있는지 경험 공유 요청드립니다. |
|
트라이 구조에서 검색어 빈도를 저장할 때, 단순 정수값으로 누적 카운팅하는 방식과 최신 검색어에 더 높은 가중치를 부여하는 방식 중에서, 또한, 최신 검색어 가중치를 부여하는 방식으로 구현할 경우 주기적인 데이터 정리를 어떤 방식으로 처리하는 것이 효율적일까? |
|
평균적인 상황 가정 외에 국가적 이슈나 외부적 사건으로 인해 검색어가 폭발적으로 증가하는 스파이크(traffic spike) 상황에 대해서는 어떻게 대비해야 할까? |
검색어 자동 시스템에서 실제 검색 요청이 작동하는 방식
tw 로 시작하는 단어가 여러개 저장해둔 rdb 테이블이 있다고 했을 때 like + orderby 검색으로 5개 limit 걸어서 요청했을 때의 시간 복잡도는 어떻게 될까? 그리고 이 방식은 왜 데이터베이스 병목을 잠재적으로 갖고 있을까?1) LIKE + ORDER BY + LIMIT 쿼리의 시간 복잡도
즉, 인덱스가 잘 구성되어 있고 LIKE 조건이 접두어 매칭으로 인덱스 범위 스캔(Range Scan)이 가능하면, 시간 복잡도는 O(log N + K)에 가깝게 나올 수 있으나, 그렇지 않다면 O(N log N) 혹은 **O(N)**에 가까운 비용이 들 수 있습니다. 2) 왜 데이터베이스 병목이 발생할 수 있는가?
결과적으로, 간단한 LIKE + ORDER BY + LIMIT 쿼리라도, 검색 자동완성과 같은 빈번한 단건 호출이 대규모로 쏟아질 경우, DB가 병목 지점이 되어 응답 지연과 서비스 성능 저하가 발생할 수 있습니다. 이런 이유로 실제 시스템 설계에서는 Redis 등 인메모리 캐시나 트라이(Trie), 서치 엔진(ElasticSearch/Solr) 등의 전문 검색 인덱스를 활용한 구조로 확장성을 확보하는 방법을 많이 사용합니다. 트라이 구조로 노드 검색을 할 때, 알고리즘을 생각해보면 p가 접두어의 길이, n이 트라이 안에 있는 노드 개수, c가 주어진 노드의 자식 노드 개수라 할 때 시간 복잡도는 O(p) + O(c) ) O(clogc)가 되는데, 이 알고리즘은 직관적이지만 최악의 경우 k개의 결과를 얻으려고 전체 트라이를 다 검색해야 할 수 있습니다. 이 문제를 해결하는 방법 두가지는 ? 그리고 시간 복잡도는 어떻게 개선될까?
=> 마찬가지로 상수가 됨 트라이 내부의 인기 검색어를 캐싱하는 방법에 대해서 설명해보자1. 데이터 구조 설계
2. 갱신(업데이트) 알고리즘트라이를 이용할 때, 보통 단어 삽입 혹은 인기도 업데이트는 “Root → 해당 단어의 마지막 노드(leaf)”까지 경로를 따라가며 처리됩니다. 이 과정을 응용하여 경로상의 모든 노드의 Top-K도 함께 갱신합니다.
그렇다면 트라이를 구성하는 삽입 삭제 연산에서 캐싱을 할때와 하지 않을 때 시간 복잡도 차이는 ?1. 캐싱하지 않는 경우삽입/삭제/인기도 변경 시
2. 캐싱을 사용하는 경우삽입/삭제/인기도 변경 시
3. 요약 비교
동시성 처리가 필요한 이유는 무엇 때문일까 ?검색어 자동완성 기능은 동시에 많은 사용자가 검색 창에 문자를 입력하고, 검색어 데이터를 갱신(인기도 업데이트 등)할 수 있기 때문에 **“여러 명이 동시에 해당 자료구조(트라이, 캐시 등)에 접근”**하는 상황이 빈번합니다. 이때 동시성 처리를 고려하지 않으면 아래와 같은 문제가 생길 수 있습니다.
결국, 많은 사용자가 동시에 조회(검색)와 갱신(인기도 변경, 새로운 검색어 등록)을 요청하는 대규모 환경에서, 트라이나 노드별 Top-K 캐시 같은 자료구조를 안전하게 다루기 위해서는 동시성 제어가 필수입니다. 이를 통해 데이터 무결성과 일관성을 유지하면서, 정상적인 검색 결과를 빠르게 제공할 수 있게 됩니다. 트라이를 저장할 DB 중 도큐먼트 DB를 사용한다면 어떻게 ?1. 기본 아이디어와 고려사항
2. 예시 스키마 설계예를 들어, MongoDB를 사용한다고 가정합시다. {
"_id": "tw", // 문서 고유 ID, 보통 prefix나 ObjectId 사용
"prefix": "tw", // 이 노드가 담당하는 접두어
"children": [
"twi", "two", "tws" // 자식 노드들의 식별자(= prefix)
],
"topK": [ // 상위 K개 인기 검색어(캐싱)
{ "word": "twitter", "freq": 120 },
{ "word": "twitch", "freq": 90 },
{ "word": "twice", "freq": 80 }
// ...
],
"updatedAt": ISODate("...") // 최근 갱신 시각 등 메타필드
}
중첩 구조 vs. 참조 구조
3. 작동 원리 (CRUD 및 검색)아래는 “문서 단위”로 트라이를 운용할 때의 일반적인 시나리오입니다. 3.1 검색(조회)
3.2 삽입(새로운 검색어 추가)예: 새 단어
3.3 인기도(freq) 변경(업데이트)예: 기존 단어 “twitter”가 한 번 더 검색돼서 인기도가 120 → 121이 되어야 한다고 하겠습니다.
3.4 삭제
4. 성능 및 확장성 이슈
5. 요약
트라이 DB를 K-V로 한다면 어떻게?키-값(K-V) 스토어를 사용해 트라이를 관리하려면, 보통 **“prefix를 Key”**로 두고, “해당 노드 정보(자식, Top-K, 메타데이터 등)를 Value로 직렬화”하는 방식으로 설계합니다.
결론적으로, “Key=접두어, Value=직렬화된 노드 정보” 방식으로 트라이를 K-V 스토어에 구성하고, 검색 시에는 Key 접근만으로 O(1)에 노드 정보를 획득해 Top-K를 반환합니다. 삽입·삭제·인기도 갱신 등은 노드 경로마다 K-V 업데이트를 수행하며, 동시성 제어나 분산 처리 방식을 적절히 설계해야 합니다. => 트라이 구조는 대개 메모리에 유지하고, DB는 백업/재구축 및 통계 분석 용도로 쓰는 형태가 많다고 함 트라이를 분산환경에서 사용할 수 있도록 규모 확장성에 대해 논의해봅시다. 단순하게 샤딩을 생각해볼 수 있는데 영어 지원을 기준으로 생각해보면, 기본적으로는 총 26개 서버가 필요할 겁니다. 그리고 그 하위의 알파벳을 샤딩하려면 이제 계층적으로 샤딩을 할 수 있겠죠? 그런데 이 방식은 그럴듯해보이지만 c로 시작하는 단어가 x보다 많다는 점을 감안할 때, 균등하게 배분하기가 어려워집니다. 그럼 이 문제를 해결하는 방법은 무엇이 있을까요 ?트라이(Trie)를 여러 노드(서버)로 분산(샤딩)해서 운영할 때, 가장 큰 이슈는 데이터(접두어) 분포가 불균등하다는 점입니다. 예컨대 1. 해시 기반 파티셔닝(Hash Partitioning)
2. 범위+해시 혼합 파티셔닝(Composite Key)
3. 동적 분할(Hot Spot Detection & Split)
4. 트라이 대신 검색 엔진(ElasticSearch/Solr) 활용
5. 결론 및 권장 패턴
결국, “균등한 분산”과 “접두어 연관성”이라는 상충된 요구사항을 어디서 타협할지가 핵심인데, 해시 또는 혼합 기법을 통해 핫스팟을 완화하면서, 필요하면 동적 재분할을 적용하는 것이 일반적인 해결책입니다. 지금까지 작성한 방식에서 트라이 갱신 주기를 1주일이나 1일로 한다고 했을 때, 실시간 변하는 검색어 추이는 반영이 안됩니다. 그렇다면 이렇게 완전 실시간 검색어 니즈를 반영하고자 한다면 어떻게 설계를 변경 / 개선해야 할까요 ?트라이(Trie)를 배치(batch) 방식으로 1일 혹은 1주 단위로만 갱신하면, 그 사이에 발생하는 **실시간 검색어 변화(특히 급상승 검색어)**를 반영할 수 없다는 문제가 있습니다. 이때 **“실시간(혹은 근실시간)으로 인기 검색어 변화를 반영”**해야 한다면, 보통 아래와 같은 하이브리드 구조나 스트리밍(Streaming) 업데이트 기법이 사용됩니다. 1. 인메모리(In-memory) 트라이 + 배치 DB(백엔드) 갱신
2. 스트리밍(실시간) 데이터 파이프라인 + 부분(Incremental) 업데이트
3. “콜드(Cold) 인덱스 + 핫(Hot) 인덱스” 이중 구조
4. 완전 실시간 트라이 업데이트 시 고려 사항
결론적으로…
트위터 같은 경우는 꽤 실시간 검색어를 반영하는 거 같은데 어떻게 하고 있나요 ?정확한 내부 구현은 트위터(Twitter)에서 공개하지 않았기 때문에 세부 사항을 100% 알 수는 없지만, 알려진 자료와 일반적인 대규모 실시간 검색 시스템의 패턴을 종합해 보면, 트위터의 실시간 검색/트렌드(Trending Topics 등)는 크게 다음과 같은 구조로 동작한다고 알려져 있습니다. 1. 트윗 스트림(데이터) 수집과 실시간 처리
2. 검색 인덱스 및 자동완성(Autocomplete)
3. 핫(Hot) vs 콜드(Cold) 인덱스 구조트위터처럼 방대한 양의 트윗 데이터를 다룰 때, “Hot-Warm-Cold” 구조를 사용하는 것이 일반적입니다:
검색 시, “Hot 인덱스 + Warm 인덱스”를 합쳐 결과를 준 뒤, 최신 트윗이나 급상승 해시태그는 Hot 인덱스 우선으로 검색하여 실시간성을 만족시키는 구조입니다. 4. 실제 트렌드 집계와 노출 로직트위터의 **트렌드(Trending Topics)**는 단순 빈도순이 아니라, “과거 대비 증가율 + 지역 + 사용자 관계” 등을 종합해 산출한다고 알려져 있습니다.
또한, 광고나 정치적 이슈를 고려해 **노출 기준(quality filter)**이 가미될 수도 있는데, 이 역시 실시간으로 조정 가능하도록 구현되어 있습니다. 5. 요약
결과적으로, 이런 스트리밍 분석 + 검색 인덱싱 + 캐싱(핫/콜드)” 조합 덕분에, 트위터는 초 단위로 폭증하는 해시태그나 키워드를 빠르게 포착해 검색 자동완성과 트렌딩에 반영할 수 있는 것입니다. |
|
같은 단어인데도 공백을 포함한 입력과 공백을 포함하지 않고 그대로 입력한 경우 같은 단어의 빈도수를 높여야 하는데 이런 경우엔 어떻게 해야 하는가? |
Uh oh!
There was an error while loading. Please reload this page.
[1부 - 테크루타*]
-> 여러분은 책만(또는 이북) 가지고 계시면 되며, 모두 함께 모여 1시간 동안 읽어 나갑니다.
-> 이 시간에는 책을 읽으며, 궁금한 것을 리서치하는 시간입니다.
-> 리서치는 짝꿍과 Pair(최소 2인/최대 3명) 로 진행됩니다. (Pair간 질문이 허용됩니다. 그러나 산만해지는 것을 방지하기 위해 4인 이상의 Group으로 진행하지 않습니다.)
-> 이 과정에서 AI 활용과 검색을 허용합니다.
-> 1시간 내에 정해진 진도를 100% 못나가도 괜찮습니다. 이어지는 2부에서 최대한 배워가길 바랍니다.
테크루타?
나이, 성별, 계급 등에 관계없이 두 명이 짝을 지어 서로 질문하고 답하며 논쟁을 통해 진리를 찾는 학습법인 유대인의 전통적인 토론 교육 방법인 하부루타와 Tech의 합성어
[2부 - 아고라]
-> 1시간 동안 진행됩니다.
-> 1부에서 공부한 내용을 바탕으로 전체 인원과 토론하는 시간을 갖습니다. (필요에 따라 Group을 지을 수도 있습니다.)
-> 누군가 (무엇이든) 모르는 내용이 있다고 하면 친절하게 알려주시길 바랍니다!
[3부 - 정리]
-> 15분 동안 진행됩니다.
-> https://github.com/noanswer-system-design-study/noanswer/discussions
-> 노앤써 레포지토리의 Discussion에 오늘 배운 내용을 모두 정리하는 시간을 갖습니다.
-> 3부에 몰아서 정리하는 것보단 자발적으로 1부, 2부에 걸쳐 수시로 작성해두는 것을 권장합니다.
All reactions