文章目录
RDD持久化
向HDFS写数据
向HDFS写数据时,当前RDD的分区即为HDFS上的文件数。
为避免生成多个小文件,可以先重分区。
rdd.repartition(4).saveAsTextFile("hdfs://bigdata01:9000/sparkdata)
向MySql写数据
写一个工具类连接数据库
import java.sql.*;
public class JdbcUtils {
private static String url = "jdbc:mysql://bigdata01:3306/test";
private static String user = "root";
private static String pwd = "123456";
private JdbcUtils(){
}
static {
try{
Class.forName("com.mysql.jdbc.Driver");
}catch(ClassNotFoundException e){
e.printStackTrace();
System.out.println("JDBC Connection Failed!");
}
}
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, user, pwd);
}
public static void main(String[] args){
try{
Connection conn = getConnection();
String sql = "insert into textFile (value) values(?)";
PreparedStatement pstmt;
pstmt = (PreparedStatement) conn.preparedStatement(sql);
pstmt.setString(1,"test");
pstmt.executeUpdate();
free(pstmt,conn);
System.out.println("----"+conn);
}catch(SQLException e){
e.printStackTrace();
}
}
public static void free(Statement stat, Connection conn){
try{
if(stat != null)
stat.close();
}catch(SQLException e){
e.printStackTrace();
}finally{
try{
if(conn!=null) stat.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
在foreach或foreachPartition方法中调用连接数据库
rdd.foreach(line => { //foreach每条记录创建一个连接
val conn = JdbcUtils.getConnection() //实际会使用连接池
val sql = "insert into textFile (value) values(?)"
var pstmt : PreparedStatement = null
pstmt = conn.prepardStatement(sql)
pstmt.setString(1,line+"")
pstmt.executeUpdate
JdbcUtils.free(pstmt, conn)
})
rdd.foreachPartition(partitions => {//foreachPartition每个分区创建一个连接
val conn = JdbcUtils.getConnection() //实际会使用连接池
partitions.foreach(line => {
val sql = "insert into textFile (value) values(?)"
var pstmt : PreparedStatement = null
pstmt = conn.prepardStatement(sql)
pstmt.setString(1,line+"")
pstmt.executeUpdate
})
JdbcUtils.free(pstmt, conn)
})
RDD缓存
同一个RDD有多个action时可以用缓存来提高性能。缓存默认保存在内存中,或者保存在磁盘中。
缓存的存储级别StorageLevel
在StorageLevel类中定义,默认是NONE
object StorageLevel{
val NONE = new StorageLevel(false,false,false,false)//是否用磁盘、是否用内存、是否用堆外内存、是否序列化
val DISK_ONLY = new StorageLevel(true,false,false,false)
val DISK_ONLY_2 = new StorageLevel(true,false,false,false,2) //2是副本数量
val MEMORY_ONLY = new StorageLevel(false,true,false,false)
val MEMORY_ONLY_2 = new StorageLevel(false,true,false,false,2)
...
}
RDD持久化存储级别如何选择:
Spark的存储级别是为了在内存使用和CPU效率之间提供不同的权衡,具体选择哪个存储级别,可以从以下方面考虑:
如果RDDs数据适合默认存储级别(MEMORY_ONLY),那么就是用默认存储级别。此时,RDD的运算速度最快;
如果没有,请尝试使用MEMORY_ONLY_SER并选择一个快速序列化库,以使对象更节省空间,但访问速度仍然相当快。(Java和Scala);
不要持久化到磁盘,除非计算数据集的函数很耗时,或者过滤了大量数据。因为,从磁盘读取分区,可能没有重新计算分区快;
如果需要快速的故障恢复,则使用副本存储级别。
cache()和persist()的区别是什么?
cache()等价于persist(MEMORY_ONLY)
def persist(): this.type = persist(StorageLevel.MEMORY_ONLY)
def cache(): this.type = persist()
缓存什么时候失效?
Spark自动监控每个节点的缓存,并以LRU(最近最少使用)方法删除旧的数据分区。也可以手动删除缓存RDD.unpersist()。
如果内存不够用,Spark会优先保证计算内存。
RDD.cache后是否真正缓存?
否。cache之后需要action触发,如果只是一次action不需要cache。
缓存查看可以在Web页面查看storage菜单。