Hello! Welcome back to our journey into the world of parallel and distributed databases.
In the last lesson, we established how to distribute data across multiple independent nodes using partitioning strategies like range and hash partitioning. This solved the problem of storing massive datasets. Now, we'll address the next logical question: how do we efficiently process that distributed data to answer queries?
Today's lesson focuses on the core strategies for speeding up a single query by executing it in parallel. By the end of this session, you'll be able to explain inter-operator and intra-operator parallelism in a shared-nothing system. These two concepts are the fundamental building blocks of any parallel query execution engine.
A Quick Recap: The Shared-Nothing World
Remember, we're operating in a shared-nothing architecture. Each node is an independent computer with its own CPU, memory, and disk, connected only by a network.

Because our data is partitioned across these nodes, we have a natural opportunity for parallelism. Instead of one machine doing all the work, we can have many machines working on their piece of the data simultaneously. This is the essence of intra-query parallelism: making a single query run faster by parallelizing its execution.
Let's dive into the two primary ways this is achieved.
To start, let's get formal definitions for the two types of intra-query parallelism. This note from UC Berkeley's CS186 course provides a clear and concise explanation.
Please read the section titled 'Query Parallelism' (Section 2). Focus on the definitions of Inter-Operator and Intra-Operator parallelism.
Now, let's break down these concepts with more detail and examples.
1. Inter-Operator Parallelism (Pipelined Parallelism)
As the name suggests, inter-operator parallelism involves executing different operators within the same query plan concurrently. Think of it as a factory assembly line.
Imagine a simple query: SELECT * FROM sales WHERE amount > 1000;
A query optimizer might generate a simple plan: Scan(sales) -> Filter(amount > 1000).
With inter-operator parallelism, the Scan operator doesn't need to finish reading the entire sales table before the Filter operator can begin. As soon as Scan produces its first row (or page of rows), it can "pipe" that output directly to the Filter operator, which can then immediately evaluate the condition amount > 1000.
This creates a pipeline of operators, where data flows through the stages of the query plan. It's also known as pipelined parallelism or vertical parallelism.

The "Bubble" Problem
This pipeline approach is efficient, but it's not perfect. A pipeline is only as fast as its slowest stage. If one operator in the pipeline is very slow, or if it's a blocking operator (like Sort, which must consume all its input before it can produce any output), the downstream operators will be starved for data, sitting idle. This idle time is often called a "pipeline bubble."
Interestingly, this exact concept and terminology are central to another area of large-scale systems: training massive machine learning models.
Stanford CS336 Language Modeling from Scratch | Spring 2025 | Lecture 7: Parallelism 1
The concept of pipelined parallelism is universal in distributed systems. This clip from a Stanford lecture on training large language models introduces the same idea, which they call 'pipeline parallel'. Notice how they describe the exact same 'bubble' problem of idle resources.
Watch from 00:45:13 to 00:48:47. The context is different (neural network layers instead of query operators), but the principle of pipelining work and the resulting utilization problem are identical.
The key takeaway is that inter-operator parallelism allows for concurrent execution of different tasks in a query, but its efficiency can be limited by dependencies and blocking operations that break the smooth flow of the pipeline.
2. Intra-Operator Parallelism (Partitioned Parallelism)
While inter-operator parallelism runs different operators at the same time, intra-operator parallelism runs multiple instances of the same operator on different subsets of data concurrently. This is where our data partitioning from the last lesson becomes critical.
This is also known as partitioned parallelism or horizontal parallelism.
Let's go back to our query: SELECT * FROM sales WHERE amount > 1000;
Assume the sales table is partitioned across 10 nodes. With intra-operator parallelism, the database can launch 10 instances of the Scan operator and 10 instances of the Filter operator. Each pair of Scan -> Filter runs on one of the 10 nodes, processing its local partition of the sales table completely in parallel.

The Exchange Operator: The Glue for Parallelism
Intra-operator parallelism is straightforward when an operation can be performed locally on each partition (like a scan or a filter). But what happens when an operator needs data from multiple partitions? For example, a JOIN between two partitioned tables, or a GROUP BY aggregation.
This is where a special operator called Exchange (or Shuffle/Repartition) comes in. The Exchange operator is responsible for moving data across the network between nodes. It's the physical implementation of data redistribution within a query plan.
As detailed in the CMU notes (LINK), the Exchange operator has three main forms:
- Gather: Merges data from many parallel workers into a single output stream on one worker. This is used at the end of a query to collect final results.
- Distribute: Splits the output of one worker and distributes it to many workers.
- Repartition / Shuffle: Reorganizes data from M input workers to N output workers, typically based on a hash function on a specific key (e.g., the join key or group by key). This is the workhorse behind parallel joins and aggregations.
Combining Both Types of Parallelism
The true power of a parallel DBMS comes from combining both inter- and intra-operator parallelism in a single query plan.
Consider this query on a shared-nothing system where users and orders are partitioned by user_id across 4 nodes:
SELECT
u.country,
COUNT(o.order_id)
FROM users u
JOIN orders o ON u.user_id = o.user_id
WHERE u.signup_date > '2023-01-01'
GROUP BY u.country;
A parallel execution plan might look like this:
- Intra-operator Parallelism: On each of the 4 nodes, a
ScanandFilterpipeline runs on the localuserspartition. At the same time, aScanruns on the localorderspartition. - Inter-operator Parallelism: The
Scanonuserspipes its output directly to theFilter. - Intra-operator Parallelism: Since both tables are partitioned on
user_id, theJOINcan be performed locally on each node in parallel. This is called a "co-located join." Each of the 4 nodes joins its localusersdata with its localordersdata. - Exchange (Shuffle): The results of the local joins now need to be aggregated by
country. The query engine initiates a shuffle. Each of the 4 nodes hashes thecountryfor its intermediate results and sends the rows to the appropriate destination node. For example, all rows for 'USA' might go to Node 1, 'Canada' to Node 2, and so on. - Intra-operator Parallelism: Each node now has a subset of the joined data, grouped by country. It performs a partial
COUNT(*)aggregation in parallel. - Exchange (Gather): Finally, the partial counts from each node are gathered to a single coordinator node, which sums them up to produce the final result.
This example shows how the two types of parallelism work together, with the Exchange operator orchestrating the necessary data movement between parallel stages.
Test your understanding!
You are tasked with executing the following query on a large products table partitioned across 100 nodes by category_id:
SELECT AVG(price) FROM products WHERE rating > 4.5;
Describe how a parallel DBMS would likely use both inter- and intra-operator parallelism to execute this query. Mention where an Exchange operator would be needed.
Show answer
-
Intra-Operator & Inter-Operator Parallelism (Local Phase):
- The system would start 100 parallel worker processes, one for each partition on each of the 100 nodes.
- On each node, a
Scanoperator reads the local partition of theproductstable. - Inter-operator (pipelined) parallelism occurs as the
Scanoperator immediately sends its output rows to aFilteroperator on the same node. - The
Filteroperator checks the conditionrating > 4.5. - The filtered rows are then piped to a local
Aggregateoperator, which calculates a partial sum of prices and a partial count of rows for that partition. This wholeScan -> Filter -> Partial Aggregatepipeline runs concurrently on all 100 nodes (intra-operator parallelism).
-
Exchange (Gather) Phase:
- After the local aggregations are complete, we have 100 pairs of (partial_sum, partial_count).
- An Exchange (Gather) operator is needed to collect these 100 partial results and send them to a single coordinator node.
-
Final Aggregation Phase:
- The coordinator node receives all the partial results. It sums all the
partial_sumvalues to get atotal_sumand sums all thepartial_countvalues to get atotal_count. - Finally, it computes the final average by dividing
total_sumbytotal_count.
- The coordinator node receives all the partial results. It sums all the
Conclusion
In this lesson, we dissected the two fundamental strategies for parallel query execution that allow distributed databases to leverage the power of a shared-nothing cluster.
Key Takeaways:
- Intra-query parallelism is about speeding up a single query.
- Inter-operator (Pipelined) Parallelism executes different operators of a query plan concurrently, like an assembly line. Its main weakness is its sensitivity to slow or blocking operators.
- Intra-operator (Partitioned) Parallelism executes multiple instances of the same operator on different data partitions simultaneously. This is the key to achieving massive scalability in shared-nothing systems.
- The Exchange (or Shuffle) operator is the critical component that moves and reorganizes data between nodes, enabling complex parallel operations like joins and aggregations.
Preview of the Next Lesson
We've now covered the "what" and "how" of parallel execution conceptually. In our next lesson, we will apply these principles to a cornerstone algorithm of any parallel database: the join. We will design and analyze a parallel hash join algorithm, including data shuffling and skew handling, which is a perfect real-world application of the concepts we learned today.