PySpark RDD Fundamentals
Narrow vs Wide Transformation Partition Mapping
RDD Lineage Fault Tolerance
RDD Architecture Overview
RDD Transformation DAG
Narrow vs Wide Transformations
Code Examples
Basic RDD Operations
from pyspark import SparkContext, SparkConf
conf = SparkConf().setAppName("RDD_Basics").setMaster("local[*]")
sc = SparkContext(conf=conf)
# Create RDD from collection
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
rdd = sc.parallelize(data, 4) # 4 partitions
print(f"Partitions: {rdd.getNumPartitions()}")
# Narrow transformations (no shuffle)
mapped = rdd.map(lambda x: x * 2)
filtered = rdd.filter(lambda x: x > 5)
flattened = rdd.flatMap(lambda x: [x, x * 10])
# Wide transformation (shuffle)
pairs = rdd.map(lambda x: (x % 3, x))
grouped = pairs.groupByKey()
reduced = pairs.reduceByKey(lambda a, b: a + b)
# Actions (trigger execution)
print(f"Count: {rdd.count()}")
print(f"First: {rdd.first()}")
print(f"Take: {rdd.take(3)}")
print(f"Sum: {rdd.reduce(lambda a, b: a + b)}")
# Check lineage
print(rdd.toDebugString().decode())
sc.stop()
RDD Persistence Levels
from pyspark import StorageLevel
# Cache in memory (deserialized)
rdd.cache() # Equivalent to persist(StorageLevel.MEMORY_ONLY)
# Persist with specific storage level
rdd.persist(StorageLevel.MEMORY_AND_DISK)
rdd.persist(StorageLevel.MEMORY_ONLY_SER)
rdd.persist(StorageLevel.DISK_ONLY)
rdd.persist(StorageLevel.OFF_HEAP)
# Unpersist when done
rdd.unpersist()
Key Concepts Table
| Concept | Description | Example |
|---|---|---|
| Partition | Logical chunk of data for parallel processing | rdd.getNumPartitions() |
| Lineage | DAG of transformations for fault tolerance | rdd.toDebugString() |
| Narrow Transform | 1:1 partition mapping, no shuffle | map(), filter(), flatMap() |
| Wide Transform | M:N partition mapping, requires shuffle | groupByKey(), reduceByKey(), join() |
| Lazy Evaluation | Transforms built but not executed until action | Build DAG β Action triggers execution |
| Action | Triggers computation, returns result | collect(), count(), first() |
| Cache/Persist | Store RDD in memory/disk for reuse | rdd.cache() or rdd.persist() |
| Checkpoint | Write RDD to reliable storage, truncate lineage | rdd.checkpoint() |
| Broadcast | Read-only variable cached on each executor | sc.broadcast(variable) |
| Accumulator | Write-only variable for aggregations | sc.accumulator(0) |
Best Practices
- Prefer DataFrames over RDDs β DataFrames use Catalyst optimizer and Tungsten engine for automatic optimization
- Use
reduceByKeyovergroupByKeyβreduceByKeycombines locally before shuffle, reducing network I/O - Cache wisely β Only cache RDDs that are reused across multiple actions
- Partition appropriately β Aim for 128MBβ200MB per partition
- Avoid
collect()on large datasets β usetake(n)orforeach()instead - Use
coalesce()to reduce partitions β avoids full shuffle unlikerepartition() - Enable Kryo serialization β 10x faster than Java serialization
- Monitor shuffle spill β indicates memory pressure
Key Takeaways
See Also
- SparkSession Architecture β The entry point that creates and manages RDDs
- DataFrame Operations β Higher-level API with Catalyst optimizations
- SparkSQL Engine β SQL queries over RDDs and DataFrames
- Transformation Types β Comprehensive guide to all transformations
- Kafka Architecture β Stream processing with RDD-based DStreams