RDD

RDD的过程就像一个水龙头,前面的那些只是状态,像水流一样向下,直到“动作”之后,才会运算的到结果。

RDD的过程是一个有向无环图(DAG)(不会有死循环)

RDD编程基础

1.RDD创建

(1)从文件系统中加载数据创建(用的是textFile()方法)

本地👇

sc.textFile("file:///usr/local/code/word.txt")	//sc这个对象就是SparkContext

如果是从HDFS上加载数据👇

sc.textFile("hdfs://localhost:9000/user/hadoop/word.txt")	//默认到本用户找(/user/hadoop)

(2)从集合创建

2.RDD操作

两种操作:

  • 转换操作
  • 行动操作

转换操作只是记录轨迹,还没有拧水龙头,也就是没有进行真正的计算。只有发生动作(要产生输出了,水龙头拧开),才会从左向右的计算。这个也就是惰性操作

转换操作:filter过滤,map映射,flatMap拍扁得到每一个单词,groupByKey分组,reduceByKey按照key进行规约......(转换操作得到的结果都是RDD,RDD可能不一样)

行动操作:count(),collect(),reduce(),first()......(得到一个结果。这就是行动操作,行动操作触发计算)

持久化(deepSeek生成)

链太长了,遇到第一个动作才会触发持久化。

RDD持久化(Persistence)是Spark中一个重要的优化技术,它允许将RDD的计算结果保存在内存或磁盘中,以便在后续操作中重复使用,避免重复计算。

持久化机制

  1. 惰性求值与持久化触发

    • RDD转换操作是惰性的,只有在遇到第一个动作(action)时才会触发实际计算
    • 持久化操作(persist()cache())也遵循这一规则,只有在第一个动作执行后才会真正缓存数据
  2. 持久化级别

    MEMORY_ONLY         // 只存储在内存中(默认)
    MEMORY_AND_DISK     // 先存内存,内存不足再存磁盘
    DISK_ONLY           // 只存储在磁盘
    MEMORY_ONLY_SER     // 序列化存储在内存
    MEMORY_AND_DISK_SER // 序列化存储,内存不足时存磁盘
    OFF_HEAP            // 存储在堆外内存
    
  3. 使用方法

    val rdd = sc.textFile("data.txt").cache()  // 等价于.persist(StorageLevel.MEMORY_ONLY)
    
    // 或者明确指定存储级别
    import org.apache.spark.storage.StorageLevel
    val rdd = sc.textFile("data.txt").persist(StorageLevel.MEMORY_AND_DISK)
    

分区(deepSeek生成)

分区(Partitioning)是RDD并行计算的基础,决定了数据如何分布在集群中。

分区机制

  1. 分区特点

    • 每个分区对应一个任务(task)
    • 分区数决定并行度
    • 合理分区能显著提高性能
  2. 分区类型

    • 默认分区:根据输入数据源自动确定
    • Hash分区:根据键的哈希值分配
    • Range分区:根据键的范围分配
    • 自定义分区:实现Partitioner接口
  3. 分区操作

    // 查看分区数
    rdd.partitions.size
    
    // 重新分区
    val repartitioned = rdd.repartition(10)  // 增加分区(触发shuffle)
    val coalesced = rdd.coalesce(5)         // 减少分区(默认不触发shuffle)
    
    // 自定义分区
    val partitioned = rdd.partitionBy(new MyPartitioner(5))
    

持久化与分区的关系(略)(deepSeek生成)

  1. 持久化会保存RDD的分区信息
  2. 重新分区后,如果不持久化,后续操作会重新计算
  3. 合理分区能提高持久化效率,减少数据倾斜

最佳实践(略)(deepSeek生成)

  1. 对需要多次使用的RDD进行持久化
  2. 根据集群资源和数据大小选择合适的持久化级别
  3. 避免过度分区导致小文件问题
  4. 数据倾斜时考虑自定义分区
  5. 持久化后建议立即执行一个action操作来触发实际缓存

示例:

val data = sc.textFile("largefile.txt")
             .map(_.split(","))
             .filter(_.length == 5)
             .persist(StorageLevel.MEMORY_AND_DISK)
             
data.count()  // 触发持久化

// 后续操作可以重用持久化的RDD
val result1 = data.map(...).reduce(...)
val result2 = data.filter(...).count()

一个例子

spark-shell
// 1. 读取文件(确保路径正确!)
val lines = sc.textFile("file:///home/xdt/桌面/data.txt")
 
// 2. 拆分单词 + 统计
val wordCounts1 = lines.flatMap(line => line.split(" "))  // 按空格分割
val wordCounts2 = wordCounts1.map(word => (word, 1))           // 每个单词计数 1
val wordCounts3 = wordCounts2.reduceByKey(_ + _)               // 相同单词累加
 
// 3. 显示结果(前20个)
wordCounts3.take(20).foreach(println)
 
// 4. 保存结果
wordCounts3.saveAsTextFile("file:///home/xdt/桌面/wordcount_output")

Spark RDD API(scala) - jinggangshan - 博客园

3.持久化(缓存)

链太长了,遇到第一个动作才会触发持久化。(遇到行动操作,触发一次真正的从头到尾的计算。从头到尾计算那肯定代价很大,只要把持久化的RDD数据保存到内存,也就是缓存)

可以使用persist()方法对一个RDD标记为持久化。这个方法也是惰性的,要等到第一个动作触发才触发持久化。

rdd.cache()和persist()可以看成等价

4.分区

如何把RDD数据分到很多台机器上。

RDD分区原则:使分区的个数尽量等于集群中CPU核心(core)数。

键值对RDD

1.键值对RDD的创建

使用map()函数创建键值对RDD

2.常用的键值对RDD转换操作

reduceByKey(func) 返回的是按key聚合后的结果(如求和、计数等)

groupByKey() 返回的是按key分组后的值集合(未聚合)

sortByKey() 按照key排序

sortBy() 更灵活的排序

mapValues(func) 对每个value都应用一个函数,key不会变

join 内连接 (K,V1)和(K,V2)=>(K,(V1,V2))

posted @ 2025-05-22 08:15  快乐星猫i  阅读(16)  评论(0)    收藏  举报