> ## Documentation Index
> Fetch the complete documentation index at: https://rajanand.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Spark: Resilient Distributed Datasets (RDDs)

RDDs, or Resilient Distributed Datasets, are the fundamental data structure in Spark (although [DataFrames](/spark/spark-dataframe) and [Datasets](/spark/spark-dataset) are now more commonly used for structured data).  Understanding RDDs is crucial for grasping Spark's core functionality and how it achieves distributed computation.

## **Key Characteristics of RDDs**

* **Immutable:** Once an RDD is created, it cannot be modified.  Any operation on an RDD creates a *new* RDD. This immutability is key to Spark's fault tolerance.
* **Distributed:**  RDDs are distributed across the cluster's nodes, enabling parallel processing.  Each partition of an RDD resides on a different executor.
* **Fault-Tolerant:**  RDDs maintain lineage (a record of how they were created).  If a partition is lost, Spark can automatically reconstruct it using the lineage information. This makes Spark resilient to node failures.
* **Partitioned:**  An RDD is divided into multiple partitions, which are the units of parallel computation.  The number of partitions influences the level of parallelism.
* **Lazy Evaluation:**  Transformations on RDDs are not executed immediately.  They are only executed when an action is called.  This allows Spark to optimize the execution plan.

## **Creating RDDs**

RDDs can be created in two main ways:

1. **Parallelizing an existing collection:**  You can create an RDD from a collection (list, tuple, etc.) in your driver program.  Spark then distributes this collection across the cluster.

   ```python theme={"system"}
   data = [1, 2, 3, 4, 5]
   rdd = sc.parallelize(data) # sc is the SparkContext
   ```

2. **Loading data from an external source:**  You can load data from various sources like HDFS, S3, local files, etc., to create an RDD.

   ```python theme={"system"}
   rdd = sc.textFile("path/to/file.txt")
   ```

## **RDD Operations**

RDD operations are broadly categorized into two types:

* **Transformations:**  These operations transform an existing RDD into a new RDD.  They are lazy; they don't execute until an action is called.  Examples include:
  * `map`: Applies a function to each element.
  * `filter`: Filters elements based on a condition.
  * `flatMap`: Applies a function that returns multiple elements for each input element.
  * `join`: Joins two RDDs based on a key.
  * `groupBy`: Groups elements based on a key.
  * `sortByKey`: Sorts an RDD by key.
  * `union`: Combines two RDDs.
  * `intersection`: Finds the common elements between two RDDs.

* **Actions:**  These operations trigger the computation and return a result to the driver.  They are eager; they execute immediately.  Examples include:
  * `collect`: Returns all elements of the RDD to the driver (use cautiously for large datasets).
  * `count`: Returns the number of elements in the RDD.
  * `take(n)`: Returns the first `n` elements of the RDD.
  * `first`: Returns the first element of the RDD.
  * `reduce`: Applies a function cumulatively to the elements of the RDD.
  * `saveAsTextFile`: Saves the RDD to a file.

## **Example**

```python theme={"system"}
from pyspark import SparkContext

sc = SparkContext("local", "RDDExample") # Creates a local SparkContext

data = [1, 2, 3, 4, 5, 6]
rdd = sc.parallelize(data)

# Transformation: Square each element
squared_rdd = rdd.map(lambda x: x * x)

# Action: Collect the results
result = squared_rdd.collect()
print(result)  # Output: [1, 4, 9, 16, 25, 36]

```

## **Limitations of RDDs:**

While RDDs are fundamental, they have limitations, especially when dealing with structured data:

* **Lower-level abstraction:**  Working directly with RDDs can be more complex than using DataFrames or Datasets.
* **Less optimized for structured data:** DataFrames and Datasets offer better optimization for structured data processing.

Because of these limitations, DataFrames and Datasets are generally preferred for most Spark applications involving structured or semi-structured data.

## QnA

<AccordionGroup>
  <Accordion title="Q: What is an RDD in Spark?" defaultOpen="true">
    RDD stands for **Resilient Distributed Dataset**. It is a low-level abstraction in Apache Spark representing a distributed collection of data that is immutable and can be processed in parallel across a cluster.\
    Key properties of RDDs:

    * **Resilient:** Fault-tolerant, with the ability to recompute lost partitions.
    * **Distributed:** Data is split across multiple nodes in a cluster.
    * **Dataset:** Represents a collection of objects of the same type.
  </Accordion>

  <Accordion title="Q: How is an RDD different from a DataFrame?">
    RDDs and DataFrames differ in several key ways:

    * **Schema:** RDDs are schema-less; they are just a collection of Java/Python/Scala objects. DataFrames have named columns and types.
    * **Optimizations:** DataFrames benefit from Catalyst Optimizer and Tungsten engine, whereas RDDs do not.
    * **Ease of Use:** RDDs require more code for transformations; DataFrames provide high-level APIs with SQL-like syntax.
    * **Performance:** RDDs are slower compared to DataFrames due to lack of query optimization.
  </Accordion>

  <Accordion title="Q: When should I use RDDs instead of DataFrames?">
    Use RDDs in the following scenarios:

    1. **Unstructured Data:** When the data doesn’t fit into a tabular format or schema.
    2. **Custom Transformations:** If you need fine-grained control over low-level transformations.
    3. **Type-Safe Processing:** If you’re working with complex data types and transformations in Scala/Java.
    4. **Backward Compatibility:** For legacy Spark jobs that rely on RDDs.

    However, DataFrames or Datasets are generally recommended for new applications due to better performance and higher-level APIs.
  </Accordion>

  <Accordion title="Q: Are RDDs mutable?">
    No, RDDs are immutable. Once created, the data in an RDD cannot be changed. However, you can perform transformations to create new RDDs based on existing ones.
  </Accordion>

  <Accordion title="Q: What are the key operations on RDDs?">
    RDD operations are divided into two types:

    1. **Transformations:** These are lazy operations that create a new RDD, e.g., `map`, `filter`, `flatMap`, `join`, `union`.
    2. **Actions:** These trigger the execution of transformations and return a result, e.g., `collect`, `count`, `reduce`, `take`.

    **Example:**

    ```python theme={"system"}
    # Create an RDD from a list  
    rdd = spark.sparkContext.parallelize([1, 2, 3, 4, 5])  

    # Apply a transformation  
    rdd_filtered = rdd.filter(lambda x: x % 2 == 0)  

    # Perform an action  
    result = rdd_filtered.collect()  
    print(result)  # Output: [2, 4]  
    ```
  </Accordion>

  <Accordion title="Q: What are narrow and wide dependencies in RDDs?">
    * **Narrow Dependencies:** Each parent partition contributes to one child partition, e.g., `map`, `filter`.
    * **Wide Dependencies:** Multiple child partitions depend on multiple parent partitions, requiring a shuffle, e.g., `groupByKey`, `reduceByKey`.\
      Wide dependencies are more expensive due to shuffling, while narrow dependencies are more efficient.
  </Accordion>

  <Accordion title="Q: Are RDDs fault-tolerant?">
    Yes, RDDs are fault-tolerant. They achieve this by:

    1. Storing the lineage (the sequence of transformations to build the RDD).
    2. Recomputing lost partitions based on lineage in case of node failure.\
       This ensures resilience during distributed processing.
  </Accordion>

  <Accordion title="Q: What are some common use cases of RDDs?">
    1. **Custom Transformations:** For complex logic that isn’t easy to express using DataFrame APIs.
    2. **Unstructured Data:** Processing log files, binary data, or other non-tabular formats.
    3. **Real-Time Processing:** Use with Spark Streaming, which relies on DStreams built from RDDs.
    4. **Backward Compatibility:** When working with older versions of Spark or existing RDD-based jobs.
  </Accordion>

  <Accordion title="Q: Can I convert between RDDs and DataFrames?">
    Yes, you can convert between RDDs and DataFrames:

    * **RDD to DataFrame:** Use `toDF()` or `spark.createDataFrame()`:
      ```python theme={"system"}
      rdd = spark.sparkContext.parallelize([("Amit", 60000), ("Riya", 85000)])  
      df = rdd.toDF(["Name", "Salary"])  
      ```
    * **DataFrame to RDD:** Use the `.rdd` property:
      ```python theme={"system"}
      rdd = df.rdd  
      ```
  </Accordion>
</AccordionGroup>
