A unified analytics engine for large-scale data processing, providing high-level APIs for batch processing, streaming, machine learning, and graph computation.
I've been running Apache Spark in production for close to a decade, across EMR, Databricks, self-managed YARN, and now Kubernetes. The honest take in 2026: Spark is no longer the obvious default it was in 2018, but for workloads in the hundreds-of-gigabytes-to-terabytes range that don't fit cleanly into a warehouse, it's still the best tool I know. The challenger isn't "no-Spark" — it's warehouse-native transformation (dbt + Snowflake), lightweight engines (DuckDB, Polars) for smaller data, and Trino for federated SQL. Spark still wins when you need a unified engine for batch + streaming + ML over petabyte-scale lake storage.
At one company we migrated a nightly Spark-on-EMR pipeline (3-hour runtime, ~80 nodes) to Snowflake + dbt. Everything smaller than 500GB/day got faster and cheaper — dbt's warehouse-native approach was clearly better for classic ELT. But one job — a ~15TB clickstream aggregation with complex sessionization logic — stayed on Spark (moved to Databricks). On Snowflake it would have cost 4x more in credits. Lesson: Spark's edge is large-scale, compute-heavy, lake-resident workloads; warehouse-native ELT wins almost everywhere else.
Apache Spark is a unified, distributed analytics engine designed for large-scale data processing. It provides high-level APIs in Python (PySpark), Scala, Java, SQL, and R on top of an optimized DAG-based execution engine. Spark runs on clusters ranging from a single laptop to thousands of nodes, orchestrated by YARN, Kubernetes, Mesos, or a standalone scheduler.
Spark was created at AMPLab (UC Berkeley) in 2009 to address the pain points of Hadoop MapReduce:
map/reduce boilerplate. You can do in 10 lines of PySpark what took 100+ lines of MapReduce.df = spark.read.parquet("s3://lake/events/")
(df.filter(df.event_type == "purchase")
.groupBy("user_id")
.agg(F.sum("amount").alias("total_spend"))
.write.mode("overwrite").parquet("s3://lake/user_spend/"))show, write, count).Things that have bitten me or teams I've worked with:
EXPLAIN EXTENDED is your friend.coalesce() before write or enable optimize-write (Delta/Iceberg).collect(). Calling .collect() or .toPandas() on a large DataFrame loads it into the driver's memory — classic production incident. Use .write to storage instead, or sample first.spark.sql.autoBroadcastJoinThreshold carefully..cache() pins data in executor memory; uncaching matters. Unused cached DataFrames cause GC pressure and evictions that hurt unrelated queries.Spark is used for large-scale data processing — ETL over terabytes of data lake files, streaming ingestion with exactly-once semantics, distributed ML training, and ad-hoc SQL analytics over Parquet/Iceberg/Delta tables. Its sweet spot in 2026 is workloads in the ~500GB–petabyte range that live on object storage and need a unified engine for batch + streaming. For data that fits in a warehouse, dbt + Snowflake/BigQuery is usually simpler and cheaper; for data that fits on one machine (<100GB), DuckDB or Polars will beat Spark in both latency and cost.
For compute, yes — Spark is 10-100x faster than Hadoop MapReduce for iterative workloads and has dramatically better ergonomics (DataFrames vs map/reduce). For storage, Hadoop's HDFS has largely been replaced by cloud object storage (S3, GCS, ADLS) with Iceberg/Delta/Hudi as the table format. In 2026, most "Hadoop" deployments are either Spark on Kubernetes against S3, or Databricks. Hadoop-the-name still shows up; Hadoop-the-stack (HDFS + MapReduce + YARN) is legacy. If you're starting fresh, don't build on HDFS + MapReduce.
PySpark is the Python API for Apache Spark — the exact same Spark engine, with a Python-friendly wrapper. When you write `df.filter(...)` in PySpark, it compiles to the same Catalyst logical plan as Scala Spark and runs in the same JVM executors. The main performance difference comes from Python UDFs (which cross the JVM↔Python boundary and are slow) vs native DataFrame operations and Scala UDFs (which stay in the JVM). Best practice: use PySpark for everything, avoid Python UDFs, and use pandas UDFs with Arrow when you absolutely need Python logic.
Apache Spark is the open-source engine. Databricks is a commercial managed platform built by the creators of Spark — it bundles Spark with Photon (a C++ rewrite of execution that's ~3x faster on benchmark workloads), Delta Lake, Unity Catalog, collaborative notebooks, job scheduling, and ML infrastructure. Think of Databricks as "Spark + enterprise features + a managed runtime." You can run open-source Spark for free on EMR, Dataproc, or Kubernetes; you pay Databricks when the managed experience, Photon, and Delta/Unity are worth the premium — which for most teams over a certain scale, they are.
Yes. Apache Spark is open-source under the Apache License 2.0 — free to use, modify, and redistribute. You only pay for the underlying compute (EC2, GKE, etc.) and storage. Commercial products built on Spark — Databricks (the largest), AWS Glue, Google Dataproc Serverless — charge a premium on top of raw compute in exchange for management, optimization, and enterprise features. For small teams, self-managed open-source Spark on Kubernetes is viable. For most mid-to-large orgs, the productivity gains from Databricks or a managed cloud option outweigh the license premium.
Not replaced — displaced for certain workload sizes. DuckDB and Polars are dramatically better than Spark for single-node analytics on data that fits in RAM (or spills gracefully to disk up to ~100-500GB). They have no JVM overhead, no shuffle, and startup in milliseconds. For workloads that fit on one machine, choose them. But once you're into multi-terabyte data, streaming with exactly-once, or ML training across 100+ nodes, Spark remains the mature choice. The 2026 pattern is "right-size the engine": DuckDB/Polars for small, warehouse-native (Snowflake/BigQuery) for mid, Spark for large.
The 80/20 rule for Spark tuning: (1) minimize shuffle — pick good partition keys, use broadcast joins when one side is <100MB, use bucketing for repeated joins; (2) tune partition count — target 128-256MB per partition, too few causes under-parallelism and OOM, too many causes scheduler overhead; (3) cache smartly — only cache DataFrames reused 3+ times, always unpersist when done; (4) avoid Python UDFs — use built-in functions or pandas UDFs with Arrow; (5) watch the Spark UI — skewed tasks, large shuffles, and GC time are the usual culprits. Databricks Photon and adaptive query execution (AQE, on by default in Spark 3.x) handle many of these automatically.