当MongoDB遇见Spark

作者: RavenZZ | 来源:发表于2017-03-23 00:02 被阅读4861次

适宜读者人群

  • 正在使用Mongodb的开发者

传统Spark生态系统 和 MongoDB在Spark生态的角色

传统Spark生态系统
Spark生态系统Spark生态系统

那么Mongodb作为一个database, 可以担任什么样的角色呢? 就是数据存储这部分, 也就是图中的黑色圈圈HDFS的部分, 如下图

用MongoDB替换HDFS后的Spark生态系统
Spark+Mongodb生态系统Spark+Mongodb生态系统

为什么要用MongoDB替换HDFS

  1. 存储方式上, HDFS以文件为单位,每个文件64MB~128MB不等, 而MongoDB作为文档数据库则表现得更加细颗粒化
  2. MongoDB支持HDFS所没有的索引的概念, 所以在读取上更加快
  3. MongoDB支持的增删改功能比HDFS更加易于修改写入后的数据
  4. HDFS的响应级别为分钟, 而MongoDB通常是毫秒级别
  5. 如果现有数据库已经是MongoDB的话, 那就不用再转存一份到HDFS上了
  6. 可以利用MongoDB强大的Aggregate做数据的筛选或预处理

MongoDB Spark Connector介绍

  1. 支持读取和写入,即可以将计算后的结果写入MongoDB
  2. 将查询拆分为n个子任务, 如Connector会将一次match,拆分为多个子任务交给spark来处理, 减少数据的全量读取

MongoDB Spark 示例代码

计算用类型Type=1的message字符数并按userid进行分组
开发Maven dependency配置

这里用的是mongo-spark-connector_2.11 的2.0.0版本和spark的spark-core_2.11的2.0.2版本

    <dependency>
        <groupId>org.mongodb.spark</groupId>
        <artifactId>mongo-spark-connector_2.11</artifactId>
        <version>2.0.0</version>
    </dependency>

    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-core_2.11</artifactId>
        <version>2.0.2</version>
    </dependency>
示例代码
    import com.mongodb.spark._
    import org.apache.spark.{SparkConf, SparkContext}
    import org.bson._


    val conf = new SparkConf()
      .setMaster("local")
      .setAppName("Mingdao-Score")
      //同时还支持mongo驱动的readPreference配置, 可以只从secondary读取数据
      .set("spark.mongodb.input.uri", "mongodb://xxx.xxx.xxx.xxx:27017,xxx.xxx.xxx:27017,xxx.xxx.xxx:27017/inputDB.collectionName")
      .set("spark.mongodb.output.uri", "mongodb://xxx.xxx.xxx.xxx:27017,xxx.xxx.xxx:27017,xxx.xxx.xxx:27017/outputDB.collectionName")

    val sc = new SparkContext(conf)
    // 创建rdd
    val originRDD = MongoSpark.load(sc)

    // 构造查询
    val dateQuery = new BsonDocument()
      .append("$gte", new BsonDateTime(start.getTime))
      .append("$lt", new BsonDateTime(end.getTime))
    val matchQuery = new Document("$match", BsonDocument.parse("{\"type\":\"1\"}"))

    // 构造Projection
    val projection1 = new BsonDocument("$project", BsonDocument.parse("{\"userid\":\"$userid\",\"message\":\"$message\"}")

    val aggregatedRDD = originRDD.withPipeline(Seq(matchQuery, projection1))

    //比如计算用户的消息字符数
    val rdd1 = aggregatedRDD.keyBy(x=>{
      Map(
        "userid" -> x.get("userid")
      )
    })

    val rdd2 = rdd1.groupByKey.map(t=>{
      (t._1, t._2.map(x => {
        x.getString("message").length
      }).sum)
    })
    
    rdd2.collect().foreach(x=>{
        println(x)
    })

    //保持统计结果至MongoDB outputurl 所指定的数据库
    MongoSpark.save(rdd2)

总结

MongoDB Connector 的文档只有基础的示例代码, 具体详情需要看GitHub中的example和部分源码

参考链接

相关文章

网友评论

  • 0bc995db8ff5:请问,怎么可以将输入和输出数据文件目录在提交spark任务时输入呢
    RavenZZ:@那畔_04e1 https://github.com/scopt/scopt 可以用这个库解析参数, 很方便:smile:
    0bc995db8ff5:@RavenZZ 真棒!我试着你的学习一下
    RavenZZ:你指的是input和output的mongo uri吗? 可以通过spark-submit的时候, 通过[application-arguments]传递过去,然后解析 main函数里的args获取
  • 老蒙:请教个问题,mongodb做了sharding,spark从Mongodb读数据会支持本地优先原则吗?还是要先通过mongos?
    RavenZZ:这个我还没有试过, 可以看下他的mongo-spark 的源码
  • 开发者头条_程序员必装的App:感谢分享!已推荐到《开发者头条》:https://toutiao.io/posts/pz6ggn 欢迎点赞支持!
    欢迎订阅《服务架构》https://toutiao.io/subject/23627
  • 829087c7ad8c:想法很赞,楼主辛苦了!
    有个问题:Spark产生的输出是不是应该写到一个专用的集群,我看了代码中Input和Output的URL相同?
    //同时还支持mongo驱动的readPreference配置, 可以只从secondary读取数据
    .set("spark.mongodb.input.uri", "mongodb://xxx.xxx.xxx.xxx:27017,xxx.xxx.xxx:27017,xxx.xxx.xxx:27017/inputDB.collectionName")
    .set("spark.mongodb.output.uri", "mongodb://xxx.xxx.xxx.xxx:27017,xxx.xxx.xxx:27017,xxx.xxx.xxx:27017/outputDB.collectionName")
    RavenZZ:@最爱乒乓 对的对的,可以到不同的集群 也推荐放到不同的集群

本文标题:当MongoDB遇见Spark

本文链接:https://www.haomeiwen.com/subject/oepanttx.html