DataStore의 지연 실행 모델을 이해하는 것은 이를 효과적으로 활용하고 최적의 성능을 얻는 데 중요합니다.
지연 실행
DataStore는 지연 실행을 사용합니다. 즉, 연산은 즉시 실행되지 않고 기록된 후 최적화된 SQL 쿼리로 컴파일됩니다. 실행은 실제로 결과가 필요할 때만 이루어집니다.
예시: 지연 실행 vs 즉시 실행
from pathlib import Path
Path("sales.csv").write_text("""\
region,product,category,amount,quantity,price,date,order_id
East,Widget,Electronics,5200,10,120,2024-01-15,1001
West,Gadget,Electronics,800,5,160,2024-02-20,1002
East,Gizmo,Home,6500,3,100,2024-03-10,1003
North,Widget,Electronics,4500,6,150,2024-06-18,1004
West,Gadget,Electronics,2000,8,250,2024-09-14,1005
""")
from chdb import datastore as pd
ds = pd.read_csv("sales.csv")
# These operations are NOT executed yet
result = (ds
.filter(ds['amount'] > 1000) # Recorded, not executed
.select('region', 'amount') # Recorded, not executed
.groupby('region') # Recorded, not executed
.agg({'amount': 'sum'}) # Recorded, not executed
.sort('sum', ascending=False) # Recorded, not executed
)
# Still no execution - just building the query plan
print(result.to_sql())
# SELECT region, SUM(amount) AS sum
# FROM file('sales.csv', 'CSVWithNames')
# WHERE amount > 1000
# GROUP BY region
# ORDER BY sum DESC
# NOW execution happens
df = result.to_df() # <-- Triggers execution지연 실행의 이점
- 쿼리 최적화: 여러 작업이 하나의 최적화된 SQL 쿼리로 컴파일됩니다
- 필터 푸시다운: 필터가 데이터 소스에서 적용됩니다
- 컬럼 프루닝: 필요한 컬럼만 읽습니다
- 결정 지연: 실행 엔진은 런타임에 선택할 수 있습니다
- 계획 확인: 실행 전에 쿼리를 확인하거나 디버그할 수 있습니다
실행 트리거
실제 값이 필요해지면 실행이 자동으로 트리거됩니다:
자동 트리거
| 트리거 | 예시 | 설명 |
|---|---|---|
print() / repr() |
print(ds) |
결과 표시 |
len() |
len(ds) |
행 수 가져오기 |
.columns |
ds.columns |
컬럼 이름 가져오기 |
.dtypes |
ds.dtypes |
컬럼 타입 가져오기 |
.shape |
ds.shape |
차원 가져오기 |
.index |
ds.index |
행 인덱스 가져오기 |
.values |
ds.values |
NumPy 배열 가져오기 |
| Iteration | for row in ds |
행 순회 |
to_df() |
ds.to_df() |
pandas로 변환 |
to_pandas() |
ds.to_pandas() |
to_df의 별칭 |
to_dict() |
ds.to_dict() |
dict로 변환 |
to_numpy() |
ds.to_numpy() |
배열로 변환 |
.equals() |
ds.equals(other) |
DataStore 비교 |
예시:
# All these trigger execution
print(ds) # Display
len(ds) # 1000
ds.columns # Index(['name', 'age', 'city'])
ds.shape # (1000, 3)
list(ds) # List of values
ds.to_df() # pandas DataFrame지연 실행으로 유지되는 작업
| 작업 | 반환값 | 설명 |
|---|---|---|
filter() |
DataStore | WHERE 절 추가 |
select() |
DataStore | 컬럼 선택 추가 |
sort() |
DataStore | ORDER BY 추가 |
groupby() |
LazyGroupBy | GROUP BY 준비 |
join() |
DataStore | JOIN 추가 |
ds['col'] |
ColumnExpr | 컬럼 참조 |
ds[['col1', 'col2']] |
DataStore | 컬럼 선택 |
예시:
# These do NOT trigger execution - they stay lazy
result = ds.filter(ds['age'] > 25) # Returns DataStore
result = ds.select('name', 'age') # Returns DataStore
result = ds['name'] # Returns ColumnExpr
result = ds.groupby('city') # Returns LazyGroupBy3단계 실행
DataStore 연산은 3단계 실행 모델을 따릅니다:
단계 1: SQL 쿼리 생성 (지연 실행)
SQL로 표현할 수 있는 작업이 누적됩니다:
result = (ds
.filter(ds['status'] == 'active') # WHERE
.select('user_id', 'amount') # SELECT
.groupby('user_id') # GROUP BY
.agg({'amount': 'sum'}) # SUM()
.sort('sum', ascending=False) # ORDER BY
.limit(10) # LIMIT
)
# All compiled into one SQL query2단계: 실행 시점
트리거가 발생하면 누적된 SQL이 실행됩니다:
# Execution triggered here
df = result.to_df()
# The single optimized SQL query runs now단계 3: DataFrame 작업(있는 경우)
실행 후 pandas 전용 작업을 연달아 수행하는 경우:
# Mixed operations
result = (ds
.filter(ds['amount'] > 100) # Phase 1: SQL
.to_df() # Phase 2: Execute
.pivot_table(...) # Phase 3: pandas
)실행 계획 확인하기
실행 계획을 확인하려면 explain()을 사용하세요:
ds = pd.read_csv("sales.csv")
query = (ds
.filter(ds['amount'] > 1000)
.groupby('region')
.agg({'amount': ['sum', 'mean']})
)
# View execution plan
query.explain()Pipeline:
1. Source: file('sales.csv', 'CSVWithNames')
2. Filter: amount > 1000
3. GroupBy: region
4. Aggregate: sum(amount), avg(amount)
Generated SQL:
SELECT region, SUM(amount) AS sum, AVG(amount) AS mean
FROM file('sales.csv', 'CSVWithNames')
WHERE amount > 1000
GROUP BY region더 자세한 내용은 verbose=True를 사용하십시오:
query.explain(verbose=True)Debugging: explain()에서 전체 문서를 확인하십시오.
캐싱
DataStore는 불필요한 쿼리 반복을 피하기 위해 실행 결과를 캐시합니다.
캐싱 작동 원리
from pathlib import Path
Path("data.csv").write_text("""\
name,age,city,salary,department
Alice,25,NYC,55000,Engineering
Bob,30,LA,65000,Product
Charlie,35,NYC,80000,Engineering
Diana,28,SF,70000,Design
Eve,42,NYC,95000,Product
""")
ds = pd.read_csv("data.csv")
result = ds.filter(ds['age'] > 25)
# First access - executes query
print(result.shape) # Executes and caches
# Second access - uses cache
print(result.columns) # Uses cached result
# Third access - uses cache
df = result.to_df() # Uses cached result캐시 무효화
작업을 통해 DataStore가 변경되면 캐시가 무효화됩니다:
result = ds.filter(ds['age'] > 25)
print(result.shape) # Executes, caches
# New operation invalidates cache
result2 = result.filter(result['city'] == 'NYC')
print(result2.shape) # Re-executes (different query)수동 캐시 제어
# Clear cache
ds.clear_cache()
# Disable caching
from chdb.datastore.config import config
config.set_cache_enabled(False)SQL과 Pandas 작업 혼합하기
DataStore는 SQL과 pandas를 함께 사용하는 작업을 지능적으로 처리합니다:
SQL 호환 연산
다음 연산은 SQL로 컴파일됩니다:
filter(),where()select()groupby(),agg()sort(),orderby()limit(),offset()join(),union()distinct()- 컬럼 연산(수학, 비교, 문자열 메서드)
Pandas 전용 작업
다음 작업은 실행을 트리거하고 pandas를 사용합니다:
- 사용자 정의 함수와 함께
apply() - 복잡한 집계와 함께
pivot_table() stack(),unstack()- 실행된 DataFrame에서 수행하는 작업
하이브리드 파이프라인
# SQL phase
result = (ds
.filter(ds['amount'] > 100) # SQL
.groupby('category') # SQL
.agg({'amount': 'sum'}) # SQL
)
# Execution + pandas phase
result = (result
.to_df() # Execute SQL
.pivot_table(...) # pandas operation
)실행 엔진 선택
DataStore는 다양한 엔진을 사용해 작업을 실행할 수 있습니다:
자동 모드 (기본)
from chdb.datastore.config import config
config.set_execution_engine('auto') # Default
# Automatically selects best engine per operationchDB 엔진 강제 지정
config.set_execution_engine('chdb')
# All operations use ClickHouse SQLpandas 엔진 강제 지정
config.set_execution_engine('pandas')
# All operations use pandas자세한 내용은 구성: 실행 엔진을 참조하십시오.
성능 영향
좋음: 일찍 필터링
# Good: Filter in SQL, then aggregate
result = (ds
.filter(ds['date'] >= '2024-01-01') # Reduces data early
.groupby('category')
.agg({'amount': 'sum'})
)나쁨: 필터를 나중에 적용
# Bad: Aggregate all, then filter
result = (ds
.groupby('category')
.agg({'amount': 'sum'})
.to_df()
.query('sum > 1000') # Pandas filter after aggregation
)좋은 예: 가능한 한 일찍 컬럼 선택하기
# Good: Select columns in SQL
result = (ds
.select('user_id', 'amount', 'date')
.filter(ds['date'] >= '2024-01-01')
.groupby('user_id')
.agg({'amount': 'sum'})
)권장: SQL에 맡기세요
# Good: Complex aggregation in SQL
result = (ds
.groupby('category')
.agg({
'amount': ['sum', 'mean', 'count'],
'quantity': 'sum'
})
.sort('sum', ascending=False)
.limit(10)
)
# One SQL query does everything
# Bad: Multiple separate queries
sums = ds.groupby('category')['amount'].sum().to_df()
means = ds.groupby('category')['amount'].mean().to_df()
# Two queries instead of one모범 사례 요약
- 실행 전에 작업을 이어서 구성하십시오 - 전체 쿼리를 만든 다음 한 번만 트리거하십시오
- 가능한 한 일찍 필터링하십시오 - 원본에서 데이터를 줄이십시오
- 필요한 컬럼만 선택하십시오 - 컬럼 프루닝은 성능을 향상시킵니다
- 실행 방식을 이해하려면
explain()을 사용하십시오 - 실행 전에 디버그하십시오 - 집계는 SQL이 처리하도록 하십시오 - ClickHouse는 이에 최적화되어 있습니다
- 실행 트리거를 숙지하십시오 - 의도치 않은 조기 실행을 피하십시오
- 캐싱을 현명하게 사용하십시오 - 캐시가 언제 무효화되는지 이해하십시오