안녕~ 오늘은 Snowflake의 Cortex Search Service에 대해서 살펴보자구.
-- =============================================================
-- 1. CREATE CORTEX SEARCH SERVICE — Single-Index (기본형)
-- =============================================================
-- ★★★ 공식 구문 (암기 필수) ★★★
-- CREATE [ OR REPLACE ] CORTEX SEARCH SERVICE [ IF NOT EXISTS ] <name>
-- ON <search_column> -- 여기에서 검색을 해라.
-- [ PRIMARY KEY ( <col_name> [, ... ] ) ]
-- ATTRIBUTES <col_name> [ , ... ]
-- WAREHOUSE = <warehouse_name>
-- TARGET_LAG = '<num> { seconds | minutes | hours | days }'
-- [ EMBEDDING_MODEL = <embedding_model_name> ] -- 모델을 지정할 수도 있음
-- [ REFRESH_MODE = { FULL | INCREMENTAL } ] -- FULL vs INCREMENTAL (PK 필요!)
-- AS <query> 에서 select하는 컬럼 중 PK를 지정하면, Incremental(증분) 방식의 인덱싱 가능
-- 즉, 인덱싱 주기마다 전체가 아닌 변경된 것들만 작업하므로 비.용.절.감 !!
-- [ INITIALIZE = { ON_CREATE | ON_SCHEDULE } ] -- ON_CREATE(즉시) vs ON_SCHEDULE(다음 주기)
--
-- [ FULL_INDEX_BUILD_INTERVAL_DAYS = <num> ]
-- [ REQUEST_LOGGING = { TRUE | FALSE } ]
-- [ AUTO_SUSPEND = <num_seconds> ] -- 최소 1800초 (30분) — 이거 숫자 잘 나옴
-- [ COMMENT = '<comment>' ]
-- AS <query>;
-- [예시 1] 가장 기본적인 Search Service
CREATE OR REPLACE CORTEX SEARCH SERVICE my_db.my_schema.basic_search_svc
ON document_text -- ON 다음에 검색 대상 텍스트 컬럼!!! 1개 지정
ATTRIBUTES category, department -- 필터링 컬럼들 (OBJECT 타입 불가)
-- 여기에 지정한 컬럼만 나중에 Search Service를 조회할 때 (=SEARCH_PREVIEW 요청이 들어올 때) filter로 사용가능
WAREHOUSE = WH_ENGINEER -- 인덱싱에 사용하는 WH
TARGET_LAG = '1 hour' -- 인덱싱 주기
AS (
SELECT
document_text,
category,
department,
created_at
FROM my_db.my_schema.knowledge_base -- 이 쿼리의 결과물로 서치서비스를 만든다.
);
-- ★ ON 절: 검색 대상 텍스트 컬럼 (1개만 지정), 하이브리드 검색을 자동으로 수행
-- ★ ATTRIBUTES: 필터링에 사용할 컬럼들 (OBJECT 타입 불가)
-- ★ TARGET_LAG: 소스 데이터 변경 후 인덱스 반영까지 최대 허용 지연
-- ★ AS 절의 쿼리: SELECT 결과가 인덱스의 소스 데이터
Cortex Search는 Single-Index에서도 하이브리드 검색을 자동으로 수행합니다.
내부적으로 ON 컬럼에 대해:
- 키워드 검색 (BM25) — 정확한 단어 매칭
- 벡터 검색 (Semantic/Embedding) — 의미적 유사도 매칭
이 두 결과를 자동으로 합산(fusion)해서 최종 랭킹을 만들어 줍니다. 사용자가 별도로 "키워드만" 또는 "벡터만" 선택할 필요 없이, CREATE할 때 ON 컬럼 하나 지정하면 Snowflake가 알아서 둘 다 처리합니다.
그래서 EMBEDDING_MODEL 파라미터가 존재하는 거예요 — 벡터 검색 쪽에서 쓸 임베딩 모델을 지정하는 겁니다. 생략하면 Snowflake 기본 모델이 적용되고요.
-- [예시 2] 임베딩 모델 명시 + 로깅 활성화
CREATE OR REPLACE CORTEX SEARCH SERVICE my_db.my_schema.docs_search_svc
ON content
ATTRIBUTES author, doc_type, status
WAREHOUSE = WH_ENGINEER
TARGET_LAG = '30 minutes'
EMBEDDING_MODEL = 'snowflake-arctic-embed-l-v2.0'
REQUEST_LOGGING = TRUE
COMMENT = 'Company document search service'
AS (
SELECT
content,
title,
author,
doc_type,
status,
updated_at
FROM my_db.my_schema.documents
WHERE status = 'PUBLISHED'
);
-- ★ EMBEDDING_MODEL: 지정하지 않으면 Snowflake가 자동 선택
-- ★ REQUEST_LOGGING = TRUE: 쿼리 모니터링용 로그 기록
[예시2]에서 REQUEST_LOGGING = TRUE: 쿼리 모니터링용 로그 기록 옵션은 무슨 뜻이야? 이 서치서비스를 쿼리할 때마다 로그를 남겨주는 건가?
네, 정확합니다. REQUEST_LOGGING = TRUE를 켜면 이 Search Service에 SEARCH_PREVIEW 요청이 들어올 때마다 로그를 남겨줍니다.
기록되는 내용:
- 누가 검색했는지
- 어떤 query를 보냈는지
- 필터 조건은 뭐였는지
- 언제 요청했는지
조회는 이렇게 합니다:
SELECT *
FROM TABLE(
INFORMATION_SCHEMA.CORTEX_SEARCH_REQUEST_LOG(
SERVICE_NAME => 'MY_SEARCH_SVC',
START_TIME => DATEADD('hour', -24, CURRENT_TIMESTAMP())
)
);
활용 목적:
- 사용자들이 실제로 뭘 많이 검색하는지 파악 (검색 패턴 분석)
- 검색 품질 개선 — 자주 검색되는데 결과가 안 좋은 키워드 식별
- Agent 연동 시 어떤 질문이 Search로 라우팅되는지 모니터링
시험 포인트: 기본값은 FALSE입니다. 명시적으로 켜야 로그가 쌓입니다.
일반적인 패턴:
- 초기 운영 — REQUEST_LOGGING = TRUE로 켜놓고 사용자 검색 패턴 수집
- 분석/개선 — 로그 보면서 검색 품질 튜닝 (ATTRIBUTES 추가, 소스 데이터 보강 등)
- 안정화 후 — 필요 없으면 FALSE로 끄거나, 비용/저장 부담 없으면 계속 켜둠
나중에 ALTER로 언제든 on/off 전환 가능합니다:
ALTER CORTEX SEARCH SERVICE my_svc SET REQUEST_LOGGING = TRUE;
처음부터 끄고 만든 뒤, 필요할 때 켜도 되니까 부담 없이 운영하면 됩니다.
-- [예시 3] PRIMARY KEY + INCREMENTAL 리프레시
CREATE OR REPLACE CORTEX SEARCH SERVICE my_db.my_schema.incremental_svc
ON article_body
PRIMARY KEY (article_id) -- AS 이후 셀렉트절 중에 PK, PK 컬럼은 TEXT 타입만 가능!
ATTRIBUTES category, region, publish_date
WAREHOUSE = WH_ENGINEER
TARGET_LAG = '15 minutes'
REFRESH_MODE = INCREMENTAL -- 변경된 행만 처리 → 임베딩 비용 절감
FULL_INDEX_BUILD_INTERVAL_DAYS = 7 -- 증분 업데이트 누적 후 주기적 전체 빌드!
AUTO_SUSPEND = 3600 -- 쿼리 없으면 N초 후 서빙 자동 중지 (최소 1800초=30분)
AS (
SELECT
article_id,
article_body,
category,
region,
publish_date
FROM my_db.my_schema.news_articles
WHERE is_active = TRUE
);
-- ★ REFRESH_MODE:
-- - FULL: 매 리프레시마다 전체 재처리 (기본)
-- - INCREMENTAL: PK 기반 변경분만 처리
맞습니다 — PRIMARYKEY는 TEXT(VARCHAR)타입컬럼만 가능합니다.
이유는 추측하신 것과 비슷합니다:
- Cortex Search는 근본적으로 텍스트/문서 검색 엔진 — 내부적으로 텍스트 인덱싱 구조를 쓰기 때문에 PK도 텍스트 기반으로 관리
- INCREMENTAL 리프레시 시 변경분 추적용 — PK로 "이 문서가 새로 추가됐는지, 업데이트됐는지" 식별하는데, 내부 인덱스가 문자열 키로 동작
그래서 실무에서 숫자 ID를 PK로 쓰고 싶으면 캐스팅해야 합니다:
FULL vs INCREMENTAL 비용 차이:
- FULL — 매 리프레시마다 전체 데이터를 다시 인덱싱 (웨어하우스 크레딧 소모 큼)
- INCREMENTAL — 변경된 행만 인덱싱 (PK로 변경분 식별)
그래서 비용 절감 공식은:
PK 지정 → INCREMENTAL 가능 → 변경분만 처리 → 크레딧 절약
여기에 추가로 TARGET_LAG도 비용에 영향을 줍니다:
| 1 minute | 높음 (자주 리프레시) | 거의 실시간 |
| 1 hour | 중간 | 적당 |
| 1 day | 낮음 (하루 1회) | 느림 |
비용 절감 3종 세트 (시험 포인트):
- PK + INCREMENTAL — 변경분만 처리
- TARGET_LAG 넉넉하게 — 리프레시 빈도 줄임
- AUTO_SUSPEND — 쿼리 없을 때 서빙 리소스 자동 중지 (최소 1800초)
실시간성이 덜 중요한 서비스라면 이 세 가지를 조합해서 비용을 확 낮출 수 있어요~
-- [예시 4] INITIALIZE = ON_SCHEDULE (대용량 데이터)
CREATE OR REPLACE CORTEX SEARCH SERVICE my_db.my_schema.large_data_svc
ON text_content
PRIMARY KEY (doc_id)
ATTRIBUTES source_system
WAREHOUSE = WH_ENGINEER
TARGET_LAG = '2 hours'
INITIALIZE = ON_SCHEDULE
AS (
SELECT doc_id, text_content, source_system
FROM my_db.my_schema.massive_documents
);
-- ★ INITIALIZE:
-- - ON_CREATE (기본): 생성 즉시 인덱싱 시작
-- - ON_SCHEDULE: 다음 스케줄 시점에 인덱싱 시작 (대용량 시 유용)
ON_SCHEDULE을 쓰는 상황:
- 소스 테이블이 아직 비어있거나 준비 중일 때 — 서비스 DDL을 먼저 배포해놓고, 데이터 적재는 나중에 하는 경우. ON_CREATE로 하면 빈 인덱스를 만들고 크레딧만 낭비.
- 대규모 초기 데이터를 로딩 중일 때 — 데이터를 배치로 밀어넣는 중에 CREATE하면, 중간 상태로 인덱싱이 시작됨. 다 넣고 나서 한 번에 인덱싱하는 게 효율적.
- CI/CD 파이프라인에서 DDL만 먼저 배포 — 인프라 프로비저닝과 데이터 준비를 분리하고 싶을 때.
-- =============================================================
-- 2. CREATE CORTEX SEARCH SERVICE — Multi-Index (고급형)
-- =============================================================
-- ★★★ 공식 구문 (Multi-Index) ★★★
-- CREATE [ OR REPLACE ] CORTEX SEARCH SERVICE <name>
-- TEXT INDEXES <text_column_name> [ , ... ]
-- VECTOR INDEXES <column_specification> [ , ... ]
-- [ PRIMARY KEY ( <col_name> [, ... ] ) ]
-- ATTRIBUTES <col_name> [ , ... ]
-- WAREHOUSE = <warehouse_name>
-- TARGET_LAG = '<num> { seconds | minutes | hours | days }'
-- ...
-- AS <query>;
-- column_specification: <column_name> ( MODEL = '<embedding_model_name>' )
-- [예시 5] Multi-Index: 여러 텍스트 컬럼 + 벡터 컬럼
CREATE OR REPLACE CORTEX SEARCH SERVICE my_db.my_schema.multi_idx_svc
TEXT INDEXES title, body, summary
VECTOR INDEXES
title_embedding (MODEL = 'snowflake-arctic-embed-m-v1.5'),
body_embedding (MODEL = 'snowflake-arctic-embed-l-v2.0')
PRIMARY KEY (doc_id)
ATTRIBUTES category, language, priority
WAREHOUSE = WH_ENGINEER
TARGET_LAG = '1 hour'
REQUEST_LOGGING = TRUE
AS (
SELECT
doc_id,
title,
body,
summary,
title_embedding,
body_embedding,
category,
language,
priority
FROM my_db.my_schema.multi_source_docs
);
★ Single-Index vs Multi-Index 차이:
| 항목 | Single-Index | Multi-Index |
| 검색 컬럼 | ON <colimn> 1개 | TEXT INDEXES (n개) + VECTOR INDEXES (n개) |
| 임베딩 | EMBEDDING_MODEL (선택적) | 각 VECTOR INDEX에 MODEL 지정 |
| 텍스트 검색 | 1개 컬럼 | 여러 텍스트 컬럼 동시 검색 |
| 벡터 검색 | 자동 생성 | 사전 생성된 벡터 컬럼 사용 |
| 쿼리 제어 | 제한적 | 각 인덱스별 가중치(weight) 설정 가능 |
| 용도 | 단일 문서 검색 | 멀티 필드/멀티 모달 검색 |
이 비교표가 핵심입니다. 차이를 직관적으로 정리하면:
Single-Index:
- ON content → 이 1개 컬럼만 검색 대상
- 임베딩은 Snowflake가 알아서 생성해줌 (자동)
Multi-Index:
- TEXT INDEXES title, body, summary → 텍스트 검색 대상이 여러 개
- VECTOR INDEXES title_embedding(MODEL=...), body_embedding(MODEL=...) → 벡터 컬럼도 여러 개, 모델도 각각 다르게 지정 가능
핵심 차이점:
| 임베딩 생성 | Snowflake가 자동 | 내가 미리 만든 벡터 컬럼 사용 |
| 검색 대상 | 1개 컬럼 | 여러 컬럼 동시 |
| 쿼리 시 제어 | 제한적 | 인덱스별 boost(가중치) 가능 |
Multi-Index를 쓰는 실무 상황: 오래된 게시판의 많은 데이터라고 가정해보자.
- 제목은 가볍게, 본문은 깊게 검색하되 가중치를 다르게 주고 싶을 때
- 이미 임베딩을 생성해둔 테이블이 있을 때
- Q&A처럼 질문/답변/맥락 각각을 독립적으로 검색해야 할 때
시험에서는 "Single과 Multi의 차이를 고르시오" 유형으로 나올 수 있으니 이 비교표를 기억하면 됩니다. 다음으로!
Single-Index에서는 Snowflake가 텍스트를 받아서 자동으로 임베딩을 생성했죠. Multi-Index에서는:
- 내가 미리 임베딩 컬럼을 만들어놓고 (예: SNOWFLAKE.CORTEX.EMBED_TEXT_1024(...) 등으로)
- 어떤 모델로 만들었는지 MODEL = '...'로 명시
- 컬럼마다 다른 모델도 가능 — 제목은 가벼운 모델, 본문은 큰 모델
VECTOR INDEXES
title_embedding (MODEL = 'snowflake-arctic-embed-m-v1.5'), -- 가벼운 모델
body_embedding (MODEL = 'snowflake-arctic-embed-l-v2.0') -- 정밀 모델
정리:
| TEXT INDEX | 키워드 + 시맨틱 (자동 하이브리드) | O |
| VECTOR INDEX | 벡터 유사도만 | O |
전부 세밀하게 튜닝할 수 있다는 게 Multi-Index의 강점입니다. 대신 복잡하고 관리 포인트가 늘어나니까, 단순한 검색은 Single-Index로 충분합니다.
Single-Index:
ON content → content 컬럼 하나에 대해 자동으로 키워드+벡터 둘 다 적용
→ Snowflake가 알아서 하이브리드 처리
Multi-Index:
TEXT INDEXES a, b → a, b는 키워드 검색만
VECTOR INDEXES b_emb, c_emb → b_emb, c_emb는 벡터 검색만
→ 각 인덱스가 독립적으로 동작. 하나의 컬럼이 자동으로 둘 다 되는 게 아님.
하이브리드는 "쿼리 시점에 조합"으로 달성합니다:
{
"multi_index_query": {
"b": { "query": "연차 규정", "boost": 1.0 },
"b_emb": { "query": "연차 규정", "boost": 1.0 }
}
}
→ b 컬럼에 대해 텍스트 검색 결과 + b_emb 벡터 검색 결과를 합산 = 수동 하이브리드
-- [예시 6] Multi-Index: 텍스트만 여러 개 (벡터 없이)
CREATE OR REPLACE CORTEX SEARCH SERVICE my_db.my_schema.text_only_multi_svc
TEXT INDEXES question, answer, context
ATTRIBUTES topic, difficulty
WAREHOUSE = WH_ENGINEER
TARGET_LAG = '1 hour'
AS (
SELECT question, answer, context, topic, difficulty
FROM my_db.my_schema.faq_dataset
);
의도적인 경우:
- a = 짧은 제목 → 키워드 매칭이면 충분 (벡터 불필요)
- c = 이미 외부에서 만든 임베딩 → 원본 텍스트가 없음 (텍스트 검색 불가)