쿼리 플랜

쿼리 플랜

어떤 lazy 쿼리든 Polars는 두 가지 플랜을 동시에 갖고 있어요. 하나는 우리가 코드로 작성한 그대로의 단계를 담은 비최적화 플랜(non-optimized plan)이고, 다른 하나는 쿼리 옵티마이저가 변경을 적용한 최적화 플랜(optimized plan)입니다. 이 둘을 시각화하거나 텍스트로 출력해 이해할 수 있어요.

출처: 공식문서

아래 쿼리를 예로 들어 설명해 볼게요.

q1 = (
    pl.scan_csv("docs/assets/data/reddit.csv")
    .with_columns(pl.col("name").str.to_uppercase())
    .filter(pl.col("comment_karma") > 0)
)

비최적화 쿼리 플랜

Graphviz 시각화

쿼리 플랜을 시각화하려면 Graphviz를 설치하고 PATH에 추가해야 합니다. 먼저 optimized=False로 설정해 비최적화 플랜을 그려볼게요.

q1.show_graph(plan_stage="ir", optimized=False)

쿼리 플랜 시각화는 아래에서 위로 읽어야 해요. 시각화에서:

  • 각 박스는 쿼리 플랜의 한 단계(stage)를 뜻합니다.
  • sigmaSELECTION을 뜻하며 필터 조건을 나타냅니다.
  • piPROJECTION을 뜻하며 컬럼의 부분 집합을 고르는 것을 나타냅니다.

텍스트로 출력된 쿼리 플랜

비최적화 플랜은 explain(optimized=False)로도 출력할 수 있어요.

q1.explain(optimized=False)
FILTER [(col("comment_karma")) > (0)] FROM WITH_COLUMNS:
 [col("name").str.uppercase()]

    CSV SCAN data/reddit.csv
    PROJECT */6 COLUMNS

출력된 플랜 역시 아래에서 위로 읽습니다. 이 비최적화 플랜은 대략 다음과 같은 일을 뜻해요.

  • data/reddit.csv 파일을 읽는다.
  • 6개 컬럼을 모두 읽는다 (PROJECT */6 COLUMNS* 와일드카드는 모든 컬럼을 가져오라는 뜻).
  • name 컬럼을 대문자로 변환한다.
  • comment_karma 컬럼에 필터를 적용한다.

최적화 쿼리 플랜

이제 show_graph로 최적화된 플랜을 시각화해 볼게요.

q1.show_graph(plan_stage="ir")

streaming 엔진을 쓸 때는 plan_type="streaming"(기본값)으로 설정해 물리 플랜(physical plan)도 시각화할 수 있어요.

q1.show_graph(plan_stage="physical")

최적화된 플랜은 explain으로도 출력할 수 있습니다.

q1.explain()
 WITH_COLUMNS:
 [col("name").str.uppercase()]

    CSV SCAN data/reddit.csv
    PROJECT */6 COLUMNS
    SELECTION: [(col("comment_karma")) > (0)]

최적화된 플랜은 이런 일을 합니다.

  • Reddit CSV에서 데이터를 읽는다.
  • CSV를 한 줄씩 읽는 동안 comment_karma 컬럼에 필터를 적용한다.
  • name 컬럼을 대문자로 변환한다.

이 경우 쿼리 옵티마이저는 파일 전체를 메모리에 읽어들인 뒤 필터를 거는 대신, 디스크에서 CSV를 읽는 동안 필터를 적용할 수 있다는 점을 파악했어요. 이 최적화를 Predicate Pushdown이라고 부릅니다.

더 알아보기 (Learn more)

  • Polars가 수행하는 다양한 최적화의 전체 개요는 최적화 문서를 보세요.
  • lazy API와 explain 사용법은 Lazy API 문서에서 다룹니다.