我有一个非常大的mongo表,我想用spark做一些分析它,它太大了,我不想加载整个数据库。但看起来它总是扫描整个数据库并将它们拆分成大量的分区,即使我将mongo.input.query
传递给它。我使用mongo-hadoop加载它,我的代码如下所示:
val conf = new SparkConf().setAppName("Simple Application")
val sc = new SparkContext(conf)
val mongoConfig = new Configuration()
val beginDate = new Date(2016 - 1900,6,7)
println("the begin data is: =========== >" + beginDate)
val beginId = new ObjectId(beginDate, 0,0.toShort,0)
mongoConfig.set("mongo.input.uri",
"mongodb://mymongoduri/mongodb.mongocollection")
val queryStr = """{"_id": {"$gt" : {"$oid":"beginDate" }}}""".replace("beginDate", beginId.toString)
mongoConfig.set("mongo.input.query", queryStr)
mongoConfig.set("mongo.input.fields", """{ "its.src":-1, "its._id":-1, "its.cid": -1}""")
val documents = sc.newAPIHadoopRDD(
mongoConfig, // Configuration
classOf[MongoInputFormat], // InputFormat
classOf[Object], // Key type
classOf[BSONObject]) // Value type
val OUTPUT_PATH = if(ENV == Some("dev")){
s"./result"
} else{
s"s3://${OUTPUT_BUCKET}/output/graph/${beginDate}"
}
documents.saveAsNewAPIHadoopFile(
OUTPUT_PATH,
classOf[Object],
classOf[BSONObject],
classOf[BSONFileOutputFormat[Object, BSONObject]]
)
它最终导致s3中的大量空文件,这不是我预期的结果(而且浪费了很多钱)。
我已阅读该文档,它只说mongo.input.query
filter the input collection with a query
,我可以像我查询的那样加载数据吗?不只是过滤它们。
或者,我可以存储那些非空的分区吗?
答案 0 :(得分:2)
spark hadoop连接器总是读取整个集合并相应地进行分区,然后使用输入查询过滤对象。当您保存文档RDD时,它将始终保存分区,无论它是否为空。
您可以将RDD重新分区为1.或者使用documents.coalesce(1).saveAsNewAPIHadoopFile(....)
答案 1 :(得分:1)
我仔细检查了代码。我发现默认拆分器始终扫描整个数据库,而com.mongodb.hadoop.splitter.MongoPaginatingSplitter
将在执行拆分时应用查询。
而且,我发现他们的wiki中有一些配置mongo.splitter.class
:
com.mongodb.hadoop.splitter.MongoPaginatingSplitter
:
此Splitter构建增量范围查询以涵盖查询。这个Splitter需要更多的工作来计算分割边界,但是当给出 mongo.input.query 时,它比其他分割器表现更好。
所以我认为这应该是我问题的最终答案。