874 words
4 minutes
[DE Design Pattern]08-06. Dataset Materializer
6. Read Performance Optimization — Dataset Materializer
01. Pattern Overvew
비싼 쿼리의 결과를 물리적으로 저장해두고, 이후 소비자는 저장된 결과만 읽게 하는 패턴
02. Materialized View vs Table 선택
Materialized View: refresh를 데이터베이스가 관리. 자동 refresh 설정 가능하지만 즉시 반영이 보장되지 않음. BigQuery, Redshift, Databricks, Snowflake 지원.
Table: refresh를 개발자가 직접 관리. 파티셔닝, 버켓팅, 정렬 등 모든 스토리지 최적화 적용 가능. 유연하지만 운영 부담 증가.
- refresh 타이밍을 정밀하게 제어해야 하거나 스토리지 최적화가 필요하면 Table, 그렇지 않으면 Materialized View
Incremental Refresh의 핵심
- 전체 refresh는 데이터가 커질수록 비용이 급증.
- Incremental refresh는 마지막 실행 이후 변경된 데이터만 반영하여 비용을 줄일 수 있음
이게 가능하려면 두 가지 조건이 필요하다:
- 변경 시점을 식별할 수 있는 컬럼이 존재해야 함 (예:
insertion_time) - 집계 로직이 incremental하게 합산 가능해야 함 (예: count는 더하면 되지만, median은 불가)
BigQuery 자동 Refresh Materialized View
# BigQuery에서 15분마다 자동 refresh되는 materialized view# (Python BigQuery 클라이언트 기준)from google.cloud import bigquery
client = bigquery.Client()
sql = """CREATE MATERIALIZED VIEW dedp.visits.visits_enrichedOPTIONS (enable_refresh = true, refresh_interval_minutes = 15)AS SELECT v.visit_id, v.event_time, v.page, u.login, d.type AS device_typeFROM dedp.visits.raw_visits vLEFT JOIN dedp.visits.users u ON v.user_id = u.idLEFT JOIN dedp.visits.devices d ON v.device_id = d.id"""
client.query(sql).result()PostgreSQL 수동 Refresh
-- 1) Materialized View 생성CREATE MATERIALIZED VIEW dedp.windowed_visits ASSELECT v.visit_id, v.event_time, v.page, u.login, t.browserFROM dedp.visits_events vLEFT JOIN dedp.user_context u ON v.visit_id = u.visit_idLEFT JOIN dedp.tech_context t ON v.visit_id = t.visit_idWHERE v.event_time >= CURRENT_DATE - INTERVAL '21 days';
-- 2) 수동 refresh (CONCURRENTLY: refresh 중에도 읽기 가능)REFRESH MATERIALIZED VIEW dedp.windowed_visits WITH CONCURRENTLY;WITH CONCURRENTLY가 없으면 refresh 중에 해당 view를 읽을 수 없다.
- 운영 환경에서는 거의 항상
CONCURRENTLY를 사용 - unique index가 있어야 이 옵션이 동작
MERGE 기반 Incremental Refresh
-- 마지막 실행 이후 추가된 데이터만 반영MERGE INTO dedp.visits_counter AS targetUSING ( SELECT user_id, COUNT(*) AS visits FROM dedp.visits_all WHERE insertion_time > '2024-11-09T03:27:32' GROUP BY user_id) AS inputON target.user_id = input.user_idWHEN MATCHED THEN UPDATE SET count = count + input.visitsWHEN NOT MATCHED THEN INSERT (user_id, count) VALUES (input.user_id, input.visits);03. Consequences
Refresh cost: 전체 refresh는 비싼 연산. Incremental refresh로 완화하되, 모든 SQL 연산이 incremental을 지원하지는 않는다.
Data access 제어: materialized view가 원본 테이블의 접근 제어를 우회할 수 있다. 원본에 접근 권한이 없는 사용자가 materialized 결과를 통해 데이터를 볼 수 있는 보안 이슈.
Storage overhead: 데이터를 복제하므로 저장 비용 증가. 일부만 materialize하는 혼합 전략(자주 쓰는 쿼리만 materialize)으로 완화.
Concept
- Dataset Materializer : 비용이 큰 쿼리 결과를 물리적으로 저장하여 반복 읽기 성능을 최적화하는 패턴
- Materialized View : 데이터베이스가 refresh를 관리하는 구현. 자동/수동 refresh 지원
- Table 기반 Materialization : 개발자가 refresh를 직접 관리. 모든 스토리지 최적화 적용 가능
- Incremental Refresh : 마지막 실행 이후 변경된 데이터만 반영하는 refresh 방식. 전체 refresh보다 가벼움
- MERGE (Upsert) : 있으면 UPDATE, 없으면 INSERT를 한 문장으로 처리하는 SQL 연산
- CONCURRENTLY : PostgreSQL에서 materialized view refresh 중에도 읽기를 허용하는 옵션
- Auto Refresh : BigQuery, Redshift 등에서 materialized view를 자동으로 갱신하는 기능. 즉시 반영은 보장되지 않음
[DE Design Pattern]08-06. Dataset Materializer
https://yjinheon.netlify.app/posts/02de/00-de-design-pattern/08_data_storage/08-06_dataset_materializer/