포스 삼촌 2026. 7. 23. 20:55

안녕~ 오늘은 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 컬럼에 대해:

  1. 키워드 검색 (BM25) — 정확한 단어 매칭
  2. 벡터 검색 (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를 보냈는지
  • 필터 조건은 뭐였는지
  • 언제 요청했는지

조회는 이렇게 합니다:

sql
SELECT *
FROM TABLE(
    INFORMATION_SCHEMA.CORTEX_SEARCH_REQUEST_LOG(
        SERVICE_NAME => 'MY_SEARCH_SVC',
        START_TIME => DATEADD('hour', -24, CURRENT_TIMESTAMP())
    )
);

활용 목적:

  • 사용자들이 실제로 뭘 많이 검색하는지 파악 (검색 패턴 분석)
  • 검색 품질 개선 — 자주 검색되는데 결과가 안 좋은 키워드 식별
  • Agent 연동 시 어떤 질문이 Search로 라우팅되는지 모니터링

시험 포인트: 기본값은 FALSE입니다. 명시적으로 켜야 로그가 쌓입니다.

 

일반적인 패턴:

  1. 초기 운영 — REQUEST_LOGGING = TRUE로 켜놓고 사용자 검색 패턴 수집
  2. 분석/개선 — 로그 보면서 검색 품질 튜닝 (ATTRIBUTES 추가, 소스 데이터 보강 등)
  3. 안정화 후 — 필요 없으면 FALSE로 끄거나, 비용/저장 부담 없으면 계속 켜둠

나중에 ALTER로 언제든 on/off 전환 가능합니다:

sql
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)타입컬럼만 가능합니다.

이유는 추측하신 것과 비슷합니다:

  1. Cortex Search는 근본적으로 텍스트/문서 검색 엔진 — 내부적으로 텍스트 인덱싱 구조를 쓰기 때문에 PK도 텍스트 기반으로 관리
  2. INCREMENTAL 리프레시 시 변경분 추적용 — PK로 "이 문서가 새로 추가됐는지, 업데이트됐는지" 식별하는데, 내부 인덱스가 문자열 키로 동작

그래서 실무에서 숫자 ID를 PK로 쓰고 싶으면 캐스팅해야 합니다:

FULL vs INCREMENTAL 비용 차이:

  • FULL — 매 리프레시마다 전체 데이터를 다시 인덱싱 (웨어하우스 크레딧 소모 큼)
  • INCREMENTAL — 변경된 행만 인덱싱 (PK로 변경분 식별)

그래서 비용 절감 공식은:

text
PK 지정 → INCREMENTAL 가능 → 변경분만 처리 → 크레딧 절약

여기에 추가로 TARGET_LAG도 비용에 영향을 줍니다:

TARGET_LAG비용데이터 신선도
1 minute 높음 (자주 리프레시) 거의 실시간
1 hour 중간 적당
1 day 낮음 (하루 1회) 느림

비용 절감 3종 세트 (시험 포인트):

  1. PK + INCREMENTAL — 변경분만 처리
  2. TARGET_LAG 넉넉하게 — 리프레시 빈도 줄임
  3. 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을 쓰는 상황:

  1. 소스 테이블이 아직 비어있거나 준비 중일 때 — 서비스 DDL을 먼저 배포해놓고, 데이터 적재는 나중에 하는 경우. ON_CREATE로 하면 빈 인덱스를 만들고 크레딧만 낭비.
  2. 대규모 초기 데이터를 로딩 중일 때 — 데이터를 배치로 밀어넣는 중에 CREATE하면, 중간 상태로 인덱싱이 시작됨. 다 넣고 나서 한 번에 인덱싱하는 게 효율적.
  3. 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=...) → 벡터 컬럼도 여러 개, 모델도 각각 다르게 지정 가능

핵심 차이점:

SingleMulti
임베딩 생성 Snowflake가 자동 내가 미리 만든 벡터 컬럼 사용
검색 대상 1개 컬럼 여러 컬럼 동시
쿼리 시 제어 제한적 인덱스별 boost(가중치) 가능

Multi-Index를 쓰는 실무 상황: 오래된 게시판의 많은 데이터라고 가정해보자.

  • 제목은 가볍게, 본문은 깊게 검색하되 가중치를 다르게 주고 싶을 때
  • 이미 임베딩을 생성해둔 테이블이 있을 때
  • Q&A처럼 질문/답변/맥락 각각을 독립적으로 검색해야 할 때

시험에서는 "Single과 Multi의 차이를 고르시오" 유형으로 나올 수 있으니 이 비교표를 기억하면 됩니다. 다음으로!

 

Single-Index에서는 Snowflake가 텍스트를 받아서 자동으로 임베딩을 생성했죠. Multi-Index에서는:

  1. 내가 미리 임베딩 컬럼을 만들어놓고 (예: SNOWFLAKE.CORTEX.EMBED_TEXT_1024(...) 등으로)
  2. 어떤 모델로 만들었는지 MODEL = '...'로 명시
  3. 컬럼마다 다른 모델도 가능 — 제목은 가벼운 모델, 본문은 큰 모델
sql
VECTOR INDEXES
    title_embedding (MODEL = 'snowflake-arctic-embed-m-v1.5'),   -- 가벼운 모델
    body_embedding  (MODEL = 'snowflake-arctic-embed-l-v2.0')    -- 정밀 모델

정리:

인덱스 타입검색 방식boost 가능
TEXT INDEX 키워드 + 시맨틱 (자동 하이브리드) O
VECTOR INDEX 벡터 유사도만 O

전부 세밀하게 튜닝할 수 있다는 게 Multi-Index의 강점입니다. 대신 복잡하고 관리 포인트가 늘어나니까, 단순한 검색은 Single-Index로 충분합니다.

 

 

Single-Index:

text
ON content → content 컬럼 하나에 대해 자동으로 키워드+벡터 둘 다 적용

→ Snowflake가 알아서 하이브리드 처리

 

 

Multi-Index:

text
TEXT INDEXES a, b         → a, b는 키워드 검색만
VECTOR INDEXES b_emb, c_emb  → b_emb, c_emb는 벡터 검색만

→ 각 인덱스가 독립적으로 동작. 하나의 컬럼이 자동으로 둘 다 되는 게 아님.

하이브리드는 "쿼리 시점에 조합"으로 달성합니다:

json
{
  "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 = 이미 외부에서 만든 임베딩 → 원본 텍스트가 없음 (텍스트 검색 불가)