Catalyst Optimizer

Catalyst is the query optimizer inside Spark SQL. It takes the query a user wrote — as SQL or as DataFrame operations — and rewrites it into an execution plan, applying transformations to a tree representation of the query at each step. The 2015 Spark SQL paper by Armbrust and colleagues introduces it as a “highly extensible optimizer” built with Scala language features so that composable rules, code generation, and extension points are easy to add.

Four phases

The paper describes rule sets for four phases of query execution, and Spark’s EXPLAIN output still mirrors them:

PhaseWhat it settles
AnalysisResolving names against the catalog — which table, which column, what type
Logical optimizationRewrites that do not change the answer: pushing filters toward the scan, trimming unused columns, folding constants
Physical planningChoosing operators that the execution engine can run, including which join implementation
Code generationCompiling parts of the query to JVM bytecode rather than interpreting operators row by row

Rules and costs

The paper states that “Catalyst supports both rule-based and cost-based optimization,” and is specific about how the two divide the labor. Rules carry the rewriting. For the cost side, several physical plans are generated and one is chosen with a cost model — but, as the paper puts it, “at the moment, cost-based optimization is only used to select join algorithms: for relations that are known to be small, Spark SQL uses a broadcast join.”

That sentence describes Spark as of the paper, and later versions added more statistics-driven optimization, so check the behavior of the version you run. The practical lesson has not changed: the join strategy depends on size estimates. When statistics are missing or stale, the optimizer is guessing, and the plan it produces will reflect the guess rather than the data.

Why it is worth knowing about

  • The plan explains the runtime. Reading the optimized logical plan shows whether a filter reached the scan; reading the physical plan shows how many shuffles the query will do.
  • The optimizer works on what it can see. Logic hidden in a user-defined function is opaque to the rules, which is one reason expressing work in built-in operations tends to run faster.
  • Optimization is not execution. Modern Spark re-optimizes at runtime with adaptive query execution, so the plan that ran can differ from the plan that was printed.

How these plans become stages and tasks is worked through in How Spark Decides What to Run. For the same idea in a relational database, see query execution plan.

References: Michael Armbrust et al., Spark SQL: Relational Data Processing in Spark, SIGMOD 2015; Apache Spark documentation, EXPLAIN.


Discover more from Insightful Data Lab

Subscribe to get the latest posts sent to your email.

Similar Posts

Questions, corrections, or additional insights?

This site uses Akismet to reduce spam. Learn how your comment data is processed.