mapPartitions、map、foreachPartition、foreach的区别
- mapPartitions和map是transform算子,分别返回一个iterator迭代器和RDD。foreachPartition、foreach是action算子,无返回值。用于结果的输出操作
- mapPartitions、foreachPartition中定义的是一个RDD的每一个分区的统一处理逻辑,每个分区中共用的变量和对象。map、foreach中定义的是一个RDD中每一条记录的转换和处理逻辑。
- foreach中的处理逻辑是串行的,map中的处理逻辑是并行的,具体与CPU核数有关,如果是1核则是串行处理。
mapPartitions、map、foreachPartition、foreach误区和正确用法
有时我们需要在转换操作时,从数据库中查询一些数据,然后经过一系列处理在写入数据库。这时需要在mapPartitions、map、foreachPartition、foreach中使用数据库连接。
错误用法:
val dStream = periodic.foreachRDD(rdd => {
//val jedis = RedisUtil.getConnectionFromPool 数据库连接定义在这里对象会被序列化,无法使用
val pStream = rdd.mapPartitions(partition => {
val newPartition = partition.map(jsonobject => {
// val jedis = RedisUtil.getConnectionFromPool //会为每一条RDD记录操作创建一个数据库连接,会将数据库连接池用完导致TimeOut异常
//做一些处理
...
jsonobject
})
jedis.close() //map是惰性加载机制,此处会有连接对象没有被使用就被关闭的风险,导致连接对象在真正需要使用时报空指针异常
// newPartition.toIterator 此处若注释掉将导致整个mapPartitions操作失效
}).persist()
//foreachPartition连接对象的错误位置与mapPartitions一样
pStream.foreachPartition(partition => {
val mongoClient = mongoClient()
val jedis = RedisUtil.getConnectionFromPool
val pipeline = jedis.pipelined()
val commandList = new util.ArrayList[WriteModel[Document]]()
partition.foreach(line => {
//做一些处理
...
})
//尽量减少数据库连接数量,对数据库执行批处理操作
if(pipeline!=null){
pipeline.sync()
pipeline.close()
}
jedis.close()
if (commandList.size() > 0) {
val result = mongoClient.getMongoColl(dbName, devDictionaryCollName).bulkWrite(commandList, new BulkWriteOptions().ordered(false))
println("*** 批量执行结果: ***" + result.toString)
}
})
pStream.unpersist()
})
正确的用法:
val dStream = periodic.foreachRDD(rdd => {
val pStream = rdd.mapPartitions(partition => {
val jedis = RedisUtil.getConnectionFromPool //正确的连接定义位置
val newPartition = partition.map(jsonobject => {
//做一些处理
...
jsonobject
}).toList //由于map操作时惰性加载,所以如果不进行action算子触发,会导致下一行jedis.close()报空指针异常
jedis.close()
newPartition.toIterator
}).persist() //把rdd持久化到内存中用于后面多次action算子操作
//对transform的RDD执行action输出操作
pStream.foreachPartition(partition => {
val mongoClient = mongoClient()
val jedis = RedisUtil.getConnectionFromPool
val pipeline = jedis.pipelined()
val commandList = new util.ArrayList[WriteModel[Document]]()
partition.foreach(line => {
//做一些处理
...
})
//尽量减少数据库连接数量,对数据库执行批处理操作
if(pipeline!=null){
pipeline.sync()
pipeline.close()
}
jedis.close()
if (commandList.size() > 0) {
val result = mongoClient.getMongoColl(dbName, devDictionaryCollName).bulkWrite(commandList, new BulkWriteOptions().ordered(false))
println("*** 批量执行结果: ***" + result.toString)
}
})
pStream.unpersist()
})