Repository navigation
docs(db): 벡터 인덱스의 실제 동작 기록 — ANN 은 돌지 않는다 - #269
Merged
Merged
Conversation
DB 계층을 처음 점검했다. 성능 문제는 없었다. 대신 **문서와 코드가 만드는 잘못된 믿음**을 찾았다. ## 1. 문서가 ivfflat 을 적고 있었다 docs/database.md 의 DDL 은 idx_embeddings_ivfflat 이다. V8 에서 HNSW 로 교체됐는데 문서가 안 따라왔다. ## 2. HNSW·GIN 인덱스는 한 번도 쓰인 적이 없다 pg_stat_user_indexes 기준 둘 다 idx_scan = 0. 고장이 아니라 **쿼리 모양** 때문이다. DocumentEmbeddingService.search 는 항상 요청자 소유의 활성 문서로 범위를 좁혀 넘기고, 비면 조회를 건너뛴다. 그래서 리포지토리에 닿는 쿼리는 예외 없이 WHERE document_id IN (...) 를 달고 있고, 필터가 선택적이라 플래너가 btree + 정렬을 고른다. 그게 더 빠르다 — 옳은 선택이다. EXPLAIN 으로 확인: JOIN 없음·필터 없음 Index Scan using idx_document_embeddings_hnsw ACTIVE_DOC_JOIN 있음 Sort ← Nested Loop (enable_seqscan=off 로도 미사용) document_id IN 있음 btree + Sort **ANN 인덱스가 붙어 있다고 근사 최근접 탐색이 도는 게 아니다.** 실제로는 사용자 범위 안에서 정확 거리 계산 후 정렬한다. 운영 쿼리 실측 실행 0.338ms(청크 57행). ## 그래서 지금은 바꾸지 않는다 ORDER BY <=> 가 조인된 관계 위에 있으면 ANN 으로 못 민다. ACTIVE_DOC_JOIN 은 오늘 기준 아무것도 거르지 않지만(삭제 시 임베딩을 함께 지우고, 서비스가 이미 활성 문서로 좁힌다) 삭제 경로가 새면 마지막 방어선이고 비용은 0.3ms 안쪽이다. **지금 빼면 얻는 것 없이 방어선만 잃는다.** 다시 볼 조건과 방법을 적어 뒀다 — 한 세션의 청크가 수천 줄이 되어 정렬 비용이 RAG 하드 타임아웃(1.5초)에 닿기 시작하면, ANN 을 서브쿼리로 먼저 돌리고 JOIN 을 바깥으로 빼면 인덱스가 살아난다(EXPLAIN 으로 확인함). ## 함께 확인한 것 (문제 없음) FK 인덱스 누락 0건 테이블 최대 471행 / 584 kB 실제 RAG 쿼리 0.338ms
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DB 계층을 처음 점검했다. 성능 문제는 없었다. 대신 문서와 코드가 만드는 잘못된 믿음을
찾았다.
1. 문서가
ivfflat을 적고 있었다docs/database.md의 DDL 은idx_embeddings_ivfflat이다. V8 에서 HNSW 로 교체됐는데문서가 안 따라왔다.
2. HNSW·GIN 인덱스는 한 번도 쓰인 적이 없다
고장이 아니라 쿼리 모양 때문이다.
DocumentEmbeddingService.search는 항상 요청자소유의 활성 문서로 범위를 좁혀 넘기고, 비면 조회를 건너뛴다. 그래서 리포지토리에 닿는 쿼리는
예외 없이
WHERE document_id IN (...)를 달고 있고, 필터가 선택적이라 플래너가btree + 정렬을 고른다. 그게 더 빠르다 — 옳은 선택이다.
EXPLAIN으로 확인:Index Scan using idx_document_embeddings_hnswACTIVE_DOC_JOIN있음Sort←Nested Loop—enable_seqscan=off로도 미사용document_id IN (...)있음Sort운영 쿼리 실측 실행 0.338ms (계획 1.69ms, 청크 57행).
그래서 지금은 바꾸지 않는다
ORDER BY <=>가 조인된 관계 위에 있으면 플래너가 ANN 으로 밀어 넣지 못한다.ACTIVE_DOC_JOIN은 오늘 기준 아무것도 거르지 않는다 — 문서 삭제 시 임베딩을 함께 지우고(soft delete 된 문서 19개의 청크가 0개), 서비스가 이미 활성 문서로 좁힌다.
그래도 남겨 둔다. 삭제 경로가 언젠가 새면 이게 마지막 방어선이고 비용은 0.3ms 안쪽이다.
지금 빼면 얻는 것 없이 방어선만 잃는다.
다시 볼 조건과 방법을 적어 뒀다: 한 세션의 청크가 수천 줄이 되어 정렬 비용이 RAG 하드
타임아웃(
questions_rag_timeout_sec/followup_rag_timeout_sec각 1.5초)에 닿기 시작하면,ANN 을 서브쿼리로 먼저 돌리고 JOIN 을 바깥으로 빼면 인덱스가 살아난다(
EXPLAIN으로 확인함).함께 확인한 것 (문제 없음)
interview_messages471행 / 584 kB코드 변경은
ACTIVE_DOC_JOIN에 붙인 설명 주석뿐이다 — 다음 사람이 "왜 HNSW 를 안 쓰지" 로헤매거나, 반대로 "ANN 이 도니까 괜찮다" 고 오해하지 않도록.