MySQL에서 벡터 검색과 RAG를 구현하는 방법은?
핵심 요약: MySQL HeatWave에서 벡터 검색과 RAG를 SQL 중심으로 구현하는 방법을 설명해요. 수천 페이지 문서 검색과 평균 30분 민원 처리 문제를 단일 DB 아키텍처로 줄이는 접근이에요.
실제 엔터프라이즈 환경에서는 수만 건의 내부 문서를 검색해야 하는 상황이 빈번하게 발생해요. 보험 약관, 계약서, 기술 매뉴얼, 사내 정책 문서처럼 비정형 텍스트가 쌓여 있는데, 직원이 "이 조항이 어디에 있지?"라고 물으면 키워드 검색으로는 한계가 있어요.
국내 대형 보험사에서 겪었던 문제가 딱 이랬어요. 약관 문서가 수천 페이지에 달하고, 상담사가 고객 질문에 답하려면 여러 문서를 뒤져야 했어요. 키워드가 정확히 일치하지 않으면 검색 결과가 엉뚱하게 나왔고, 민원 처리 시간이 평균 30분을 넘겼어요. 별도 벡터 데이터베이스를 도입하려니 인프라 복잡도와 보안 이슈가 걸렸어요.
이 문제를 MySQL HeatWave 하나로 해결한 방법을 설명할게요. 벡터 임베딩 생성부터 RAG 파이프라인 구성, LangChain 통합까지 실제 SQL과 Python 코드로 살펴볼게요.
이 글은 MySQL HeatWave 소개 글에서 이어지는 내용이에요. HeatWave의 기본 아키텍처가 궁금하다면 먼저 읽어보세요.
키워드 검색이 실패하는 이유
"면책 조항"을 검색하면 "면책"이라는 단어가 포함된 문서만 나와요. 하지만 고객이 "사고가 나도 보험금을 못 받는 경우가 있나요?"라고 물으면, 이 질문에는 "면책"이라는 단어가 없어요. 의미는 같지만 표현이 달라요.
이게 키워드 검색의 근본적인 한계예요. 단어의 형태를 비교하지, 의미를 비교하지 않아요. 동의어, 유사 표현, 맥락을 이해하지 못해요.
벡터 검색은 다르게 작동해요. 텍스트를 수백 차원의 숫자 벡터로 변환하고, 의미적으로 가까운 벡터를 찾아요. "면책 조항"과 "보험금을 못 받는 경우"는 키워드로는 다르지만, 벡터 공간에서는 가까운 위치에 있어요. 도서관에서 책을 제목으로 찾는 것과 내용으로 찾는 것의 차이라고 생각하면 돼요.
RAG(Retrieval-Augmented Generation)는 여기서 한 발짝 더 나아가요. 벡터 검색으로 관련 문서를 찾고, 그 내용을 LLM에 컨텍스트로 제공해서 정확한 답변을 생성해요. 검색과 생성을 결합한 구조예요.
HeatWave Vector Store 아키텍처
기존 RAG 파이프라인을 구성하려면 여러 서비스가 필요해요. 문서 파싱 도구, 임베딩 API, 벡터 데이터베이스, LLM 서비스, 그리고 이것들을 연결하는 오케스트레이션 레이어까지요. 각 서비스 간 데이터 이동이 발생하고, 보안 경계가 복잡해져요.
HeatWave는 이 모든 것을 데이터베이스 안에서 처리해요.
문서를 OCI Object Storage에 올리면, VECTOR_STORE_LOAD() 프로시저가 파싱, 청킹, 임베딩 생성, 저장을 한 번에 처리해요. 생성된 임베딩은 일반 MySQL 테이블에 저장되기 때문에 기존 SQL 쿼리와 자유롭게 조인할 수 있어요.
LLM도 HeatWave 클러스터 안에서 실행돼요. 데이터가 데이터베이스 밖으로 나가지 않아요. 금융권이나 의료 분야처럼 데이터 외부 유출에 민감한 고객사에게 이 구조가 중요한 이유예요.
문서 수집부터 임베딩까지
Vector Store를 만드는 과정은 SQL 한 줄로 시작해요.
-- OCI Object Storage에서 문서를 로드하고 벡터 스토어 생성
CALL sys.VECTOR_STORE_LOAD(
'oci://my-bucket@my-namespace/insurance-docs/',
'{"table_name": "insurance_embeddings"}',
@status
);
SELECT @status;
이 프로시저가 실행되면 HeatWave가 버킷 안의 PDF, DOCX, TXT 파일을 읽고, 텍스트를 추출하고, 적절한 크기로 청킹하고, 임베딩을 생성해서 테이블에 저장해요. 기본 임베딩 모델은 multilingual-e5-small이에요. MySQL 9.3.0부터 기본값으로 설정되어 있고, 한국어를 포함한 다국어를 지원해요.
생성된 테이블 구조를 확인해볼게요.
DESCRIBE insurance_embeddings;
+------------------+--------+------+-----+---------+
| Field | Type | Null | Key | Default |
+------------------+--------+------+-----+---------+
| id | bigint | NO | PRI | NULL |
| document_name | text | YES | | NULL |
| segment_number | int | YES | | NULL |
| segment | text | YES | | NULL |
| embedding | vector | YES | | NULL |
+------------------+--------+------+-----+---------+
embedding 컬럼이 VECTOR 타입이에요. MySQL 9.0부터 지원하는 네이티브 데이터 타입이에요.
개별 텍스트에 대한 임베딩을 직접 생성할 수도 있어요.
-- 단일 텍스트 임베딩 생성
SELECT sys.ML_EMBED_ROW(
'보험금 청구 절차는 어떻게 되나요?',
JSON_OBJECT('model_id', 'multilingual-e5-small')
) AS embedding;
MySQL 9.4.2부터는 스캔된 PDF도 처리할 수 있어요. VLM(Vision Language Model) 기반 파싱이 추가되면서 표, 이미지 안의 텍스트까지 추출해요. 오래된 보험 약관처럼 스캔본으로만 존재하는 문서도 벡터화할 수 있게 됐어요.
RAG 파이프라인 구현
벡터 스토어가 준비되면 RAG 쿼리는 SQL 한 줄이에요.
-- 기본 RAG 쿼리
SET @options = JSON_OBJECT(
'vector_store', JSON_ARRAY('insurance_embeddings'),
'model_options', JSON_OBJECT('model_id', 'llama3.2-3b-instruct-v1')
);
CALL sys.ML_RAG(
'자동차 사고 시 보험금 청구 기한은 얼마나 되나요?',
@output,
@options
);
SELECT JSON_PRETTY(@output);
응답에는 생성된 답변과 함께 어떤 문서의 어느 세그먼트를 참조했는지 인용 정보가 포함돼요.
{
"text": "자동차 사고 발생 후 보험금 청구는 사고일로부터 3년 이내에 해야 합니다...",
"citations": [
{
"segment": "제15조 보험금 청구 기한...",
"distance": 0.0521,
"document_name": "auto-insurance-terms-2024.pdf",
"segment_number": 142
}
]
}
MySQL 9.5.0부터는 하이브리드 검색을 사용할 수 있어요. 시맨틱 검색과 키워드 검색을 결합해서 특정 상품명, 조항 번호처럼 정확한 매칭이 필요한 경우에도 잘 작동해요.
-- 하이브리드 검색 (MySQL 9.5.0+)
SET @options = JSON_OBJECT(
'vector_store', JSON_ARRAY('insurance_embeddings'),
'semantic_search', true,
'keyword_search', true
);
CALL sys.ML_RAG('제15조 면책 조항 내용', @output, @options);
여러 질의를 병렬로 처리해야 할 때는 ML_RAG_TABLE()을 사용해요. 배치 처리라서 단건 반복보다 훨씬 빠르게 처리돼요.
-- 배치 RAG 처리
CALL sys.ML_RAG_TABLE(
'SELECT id, question FROM customer_queries WHERE processed = 0',
'answers',
JSON_OBJECT('vector_store', JSON_ARRAY('insurance_embeddings'))
);
SQL 필터링과 벡터 검색을 결합하는 것도 가능해요. 특정 상품 카테고리의 문서만 검색하거나, 최근 업데이트된 문서를 우선 참조하는 식으로 활용할 수 있어요.
-- 벡터 검색 + SQL 필터 결합
SELECT
e.segment,
VECTOR_DISTANCE(e.embedding, sys.ML_EMBED_ROW('보험금 청구 절차')) AS distance
FROM insurance_embeddings e
JOIN document_metadata m ON e.document_name = m.file_name
WHERE m.category = 'auto'
AND m.updated_at >= '2024-01-01'
ORDER BY distance ASC
LIMIT 5;
LangChain 통합 아키텍처
SQL 인터페이스만으로도 충분하지만, 복잡한 에이전트 워크플로우나 멀티스텝 추론이 필요하면 LangChain과 통합하는 게 더 유연해요.
mysql-connector-python 9.5.0부터 mysql.ai.genai 패키지가 포함돼요. LangChain의 표준 인터페이스를 구현한 세 가지 클래스를 제공해요.
from mysql.ai.genai import MyEmbeddings, MyVectorStore, MyLLM
import mysql.connector
# HeatWave 연결
mydb = mysql.connector.connect(
host="your-heatwave-endpoint",
user="your-user",
password="your-password",
database="insurance_db"
)
# LangChain 컴포넌트 초기화
embedding_model = MyEmbeddings(mydb)
vector_store = MyVectorStore(mydb, embedding_model)
llm = MyLLM(mydb)
문서를 벡터 스토어에 추가하는 것도 LangChain 표준 방식 그대로예요.
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import PyPDFLoader
# 문서 로드 및 청킹
loader = PyPDFLoader("insurance-terms.pdf")
documents = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50
)
splits = splitter.split_documents(documents)
# HeatWave Vector Store에 저장
vector_store.add_documents(documents=splits)
RAG 체인 구성은 기존 LangChain 패턴과 동일해요. LLM만 MyLLM으로 교체하면 돼요.
from langchain import hub
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
retriever = vector_store.as_retriever(search_kwargs={"k": 5})
prompt = hub.pull("rlm/rag-prompt")
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
answer = rag_chain.invoke("자동차 사고 시 보험금 청구 기한은?")
기존 LangChain 파이프라인을 이미 운영 중인 고객사라면, LLM 클래스 한 줄만 바꾸면 HeatWave로 전환할 수 있어요. 나머지 코드는 그대로 유지돼요.
에이전트 구성도 지원해요. ZERO_SHOT_REACT_DESCRIPTION 에이전트 타입으로 멀티스텝 추론이 가능해요.
from langchain.agents import initialize_agent, AgentType
agent = initialize_agent(
tools=[search_tool, calculator_tool],
llm=MyLLM(mydb).bind(model_id="meta.llama-3.3-70b-instruct"),
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
handle_parsing_errors=True,
max_iterations=3,
verbose=True
)
pgvector, Pinecone과 무엇이 다른가요
벡터 검색 솔루션을 선택할 때 자주 비교하는 세 가지예요.
| 항목 | pgvector | Pinecone | HeatWave Vector Store |
|---|---|---|---|
| 플랫폼 | PostgreSQL 확장 | 관리형 벡터 DB | MySQL 통합 플랫폼 |
| 임베딩 생성 | 외부 API 필요 | 외부 API 필요 | In-Database (ML_EMBED_ROW) |
| LLM 통합 | 별도 서비스 필요 | 별도 서비스 필요 | In-Database LLM 내장 |
| RAG 파이프라인 | 직접 구성 | 직접 구성 | ML_RAG() 단일 함수 |
| OLTP 통합 | 동일 DB | 별도 연동 필요 | 동일 DB (MySQL) |
| 데이터 이동 | 임베딩 API 호출 시 외부 전송 | 외부 전송 | 없음 (In-Database) |
| 스케일아웃 | 수직 확장 중심 | 관리형 자동 확장 | 512 노드 수평 확장 |
| 벡터 처리 성능 | 기준 | 기준 | Snowflake 대비 29배 빠름 |
pgvector는 이미 PostgreSQL을 쓰는 팀에게 진입 장벽이 낮아요. SQL 필터링과 벡터 검색을 자연스럽게 결합할 수 있고, 오픈소스라 비용 부담도 적어요. 다만 임베딩 생성과 LLM은 외부 서비스를 별도로 연결해야 해요.
Pinecone은 대규모 벡터 데이터를 관리형으로 운영하고 싶을 때 선택해요. 인프라 관리 부담이 없지만, 벤더 종속이 생기고 데이터가 외부로 나가요.
HeatWave의 차별점은 단일 플랫폼이에요. 임베딩 생성, 벡터 저장, LLM 추론, OLTP 쿼리가 모두 같은 MySQL 인스턴스 안에서 돌아요. 데이터가 외부로 나가지 않아서 금융권 규제 환경에서 유리해요. Oracle 공식 벤치마크에서 Snowflake 대비 29배, BigQuery 대비 18배, Databricks 대비 15배 빠른 벡터 처리 성능을 기록했어요.
운영하면서 계속 느낀 건, 아키텍처 단순화가 생각보다 큰 이점이라는 거예요. 벡터 DB, 임베딩 API, LLM 서비스를 각각 관리하다 보면 장애 포인트가 늘어나고, 각 서비스 간 레이턴시가 쌓여요. HeatWave는 이 복잡도를 데이터베이스 안으로 흡수해요.
MCP로 AI 에이전트와 연결하기
Model Context Protocol(MCP)은 AI 에이전트가 외부 도구와 표준화된 방식으로 통신하는 프로토콜이에요. Oracle이 공식 MCP 서버를 제공하고 있어요.
Claude, GPT-4 같은 AI 에이전트가 HeatWave에 직접 질의할 수 있어요. 에이전트가 "이 고객의 보험 약관에서 면책 조항을 찾아줘"라고 요청하면, MCP 서버가 ask_ml_rag 도구를 호출해서 결과를 반환해요.
주요 도구는 이렇게 구성돼요.
| 도구 | 기능 |
|---|---|
ml_generate | LLM 텍스트 생성 |
ask_ml_rag | Vector Store 기반 RAG 쿼리 |
ragify_column | 테이블 컬럼을 벡터화 |
ask_nl_sql | 자연어를 SQL로 변환 후 실행 |
heatwave_ask_help | HeatWave ML 기능 안내 |
load_vector_store_oci | OCI Object Storage에서 문서 로드 |
MCP 서버 설정은 간단해요.
{
"mcpServers": {
"mysql-heatwave": {
"command": "uv",
"args": ["run", "mysql_mcp_server.py"],
"env": {
"MYSQL_HOST": "your-heatwave-endpoint",
"MYSQL_USER": "your-user",
"MYSQL_PASSWORD": "your-password"
}
}
}
}
현재 MCP 서버는 PoC 단계예요. Oracle 공식 저장소(github.com/oracle/mcp)에서 관리되고 있고, 프로덕션 배포보다는 개념 검증과 프로토타이핑에 적합해요. 프로덕션 환경에서는 SQL 인터페이스나 LangChain 통합을 사용하는 게 안정적이에요.
도입 전에 고려해야 할 것들
HeatWave Vector Store가 모든 상황에 맞는 건 아니에요. 실제로 만나게 되는 제약들이 있어요.
Vector Store의 자동화 파이프라인(VECTOR_STORE_LOAD)은 OCI Object Storage와 연동돼요. AWS나 Azure에서 HeatWave를 사용하는 경우, 문서를 OCI Object Storage로 옮기거나 다른 방식으로 임베딩을 생성해야 해요. AWS 환경에서는 Amazon Bedrock 모델을 연동하는 방식도 지원하지만, 설정이 추가로 필요해요.
내장 LLM은 Llama, Mistral 계열 오픈소스 모델이에요. GPT-4나 Claude 같은 최신 상용 모델을 쓰고 싶다면 OCI Generative AI Service를 통해 연동해야 해요. 이 경우 데이터가 OCI 서비스로 전송돼요.
기능별로 최소 MySQL 버전이 달라요. 하이브리드 검색은 9.5.0, VLM 기반 문서 파싱은 9.4.2, 기본 Vector Store는 9.0 이상이 필요해요. 기존 MySQL 버전이 낮다면 업그레이드 계획이 필요해요.
MySQL 9.5.0부터 HNSW 기반 자동 벡터 인덱스 생성을 지원해요. 그 이전 버전에서는 대규모 벡터 검색 시 풀 스캔이 발생할 수 있어요. 수백만 건 이상의 임베딩을 다룬다면 버전 확인이 중요해요.
처음에는 이렇게 생각했어요. "SQL 한 줄로 RAG가 된다니 너무 간단한 거 아닌가?" 실제로 만들어 보니 파이프라인 자체는 정말 간단했어요. 하지만 청킹 전략, 임베딩 모델 선택, 검색 결과 품질 튜닝은 여전히 도메인 지식이 필요한 작업이에요. 도구가 단순해졌다고 해서 RAG 설계가 쉬워진 건 아니에요.
핵심 요약
- HeatWave Vector Store는 문서 파싱, 임베딩 생성, 벡터 저장, LLM 추론을 MySQL 안에서 처리해요. 별도 벡터 DB가 필요 없어요.
ML_RAG()함수 하나로 RAG 파이프라인을 실행할 수 있어요. MySQL 9.5.0부터는 키워드 + 시맨틱 하이브리드 검색도 지원해요.- LangChain 통합은
mysql-connector-python9.5.0의mysql.ai.genai패키지로 제공돼요. 기존 LangChain 파이프라인에서 LLM 클래스만 교체하면 돼요. - Oracle 공식 벤치마크 기준으로 Snowflake 대비 29배, BigQuery 대비 18배 빠른 벡터 처리 성능을 보여요.
- 데이터가 데이터베이스 밖으로 나가지 않아서 금융권, 의료 분야처럼 보안 요구사항이 엄격한 환경에 적합해요.
- OCI 의존성, MySQL 버전 요구사항, In-Database LLM 모델 제약은 도입 전에 확인해야 해요.
FAQ
Q. HeatWave Vector Store를 사용하려면 기존 MySQL 데이터를 마이그레이션해야 하나요?
마이그레이션이 필요 없어요. HeatWave는 기존 MySQL 데이터베이스 위에 Vector Store 기능이 추가된 구조예요. 기존 OLTP 테이블은 그대로 유지하면서, 비정형 문서를 위한 임베딩 테이블을 별도로 생성해요. 두 테이블을 SQL JOIN으로 결합하는 것도 가능해요.
Q. pgvector를 이미 사용 중인데 HeatWave로 전환할 이유가 있나요?
pgvector는 PostgreSQL 환경에서 벡터 검색을 추가하는 좋은 방법이에요. 다만 임베딩 생성과 LLM은 외부 서비스를 별도로 연결해야 해요. HeatWave는 임베딩 생성, 벡터 저장, LLM 추론이 모두 데이터베이스 안에서 처리돼요. 데이터 외부 유출 없이 RAG를 구현해야 하거나, MySQL을 이미 사용 중인 고객사라면 전환을 검토할 만해요.
Q. 한국어 문서도 잘 처리되나요?
기본 임베딩 모델인 multilingual-e5-small이 한국어를 포함한 다국어를 지원해요. MySQL 9.3.0부터 기본값으로 설정되어 있어요. 보험 약관, 계약서, 사내 정책 문서처럼 한국어로 작성된 비정형 문서를 벡터화하고 한국어로 질의하는 것이 가능해요. 다만 In-Database LLM의 한국어 답변 품질은 영어 대비 다소 낮을 수 있어요. 고품질 한국어 답변이 필요하다면 OCI Generative AI Service의 외부 모델을 연동하는 방식을 고려해보세요.
Q. 벡터 스토어 생성 후 새 문서를 추가하면 전체를 다시 만들어야 하나요?
증분 추가를 지원해요. VECTOR_STORE_LOAD()를 새 문서에 대해 다시 실행하면 기존 임베딩은 유지하면서 새 문서만 추가돼요. 기존 솔루션들이 새 데이터 추가 시 전체 재생성이 필요했던 것과 달리, HeatWave는 증분 업데이트가 가능해요.
Q. MCP 서버는 프로덕션에서 사용할 수 있나요?
현재 Oracle 공식 MCP 서버는 PoC 단계예요. 개념 검증과 프로토타이핑에 적합하고, 프로덕션 배포에는 추가적인 보안 설정과 안정성 검증이 필요해요. 프로덕션 환경에서는 SQL 인터페이스나 LangChain 통합을 사용하는 게 권장돼요.
