每日任务Scala语言Spark读Hive表写MongoDB内嵌文档格式

Hive表:

name的时间流水数据。name数据可能是一条,可能多条数据。

nametimed1d2d3d4d5d6d7d8d9d10d11d12d13d14d15
11563764501111111111111111
11563764502111111111111111

MongoDB文档格式:

一年的数据一个Collection

一个Collection一个用户数据都在同一个name文档里面

{
    "_id" : ObjectId("5ee839352ceb11d7b9bcddbd"),
    "name" : "1",
    "records" : [
        {
            "d1" : 1,
            "d2" : "20200601",
            "d3" : 1590971827,
            "d4" : 1590972128,
            "d5" : 65027.0,
            "d6" : 65028.0,
            "d7" : 13181.0,
            "d8" : 13181.0,
            "d9" : 0.0,
            "d10" : 0.0,
            "d11" : 3770.3,
            "d12" : 3770.3,
            "d13" : 6.4,
            "d14" : 5.7,
            "d15" : 8.3,
            "_class" : "common.Records"
        },
        {
            "d1" : 1,
            "d2" : "20200601",
            "d3" : 1590984235,
            "d4" : 1590984638,
            "d5" : 65028.0,
            "d6" : 65030.0,
            "d7" : 13181.0,
            "d8" : 13181.0,
            "d9" : 0.0,
            "d10" : 0.0,
            "d11" : 3770.3,
            "d12" : 3770.3,
            "d13" : 6.4,
            "d14" : 5.7,
            "d15" : 8.4,
            "d16" : 0.3,
            "_class" : "common.Records"
        },
        {
            "d1" : 1,
            "d2" : "20200601",
            "d3" : 1590988562,
            "d4" : 1590988864,
            "d5" : 65030.0,
            "d6" : 65032.0,
            "d7" : 13181.0,
            "d8" : 13181.0,
            "d9" : 0.0,
            "d10" : 0.0,
            "d11" : 3770.3,
            "d12" : 3770.3,
            "d13" : 6.4,
            "d14" : 5.7,
            "d15" : 8.3,
            "d16" : 0.3,
            "_class" : "common.Records"
        }
    ]
}

Hive表数据量,每天2000万条+

CDH集群26节点,单节点40核128G内存磁盘总共1PB

HBase  2.1
Hive 2.1.1
Hadoop  3.0.0
spark 2.4.0

工程:

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.xxx</groupId>
  <artifactId>scala_project_26node</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>

  <!--<name>A Camel Scala Route</name>-->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.5.RELEASE</version>
  </parent>
  <properties>
    <java.source>1.8</java.source>
    <java.target>1.8</java.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <jdk.version>1.8</jdk.version>
    <geotools.version>9.4</geotools.version>
    <!--<hive.version>2.3.2</hive.version>-->
    <spark.version>2.4.0</spark.version>
  </properties>

  <dependencies>
    <!-- for Hue Jar -->
    <!--hadoop hbase-->
    <dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-common</artifactId>
    <version>2.6.0</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-hdfs</artifactId>
    <version>2.6.0</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-client</artifactId>
    <version>2.6.0</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-server</artifactId>
    <version>1.2.6</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>1.2.6</version>
    <scope>provided</scope>
    </dependency>

    <dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-core_2.11</artifactId>
    <version>${spark.version}</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-mllib_2.11</artifactId>
      <version>${spark.version}</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-graphx_2.11</artifactId>
      <version>${spark.version}</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-client</artifactId>
    <version>2.6.0</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-hive_2.11</artifactId>
      <version>${spark.version}</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>org.apache.hive</groupId>
    <artifactId>hive-hbase-handler</artifactId>
    <version>1.2.1</version>
    <scope>provided</scope>
    </dependency>
    <!--for Hue jar-->

    <!--for local test jar -->
    <!--hadoop hbase-->
    <!--<dependency>-->
      <!--<groupId>org.apache.hadoop</groupId>-->
      <!--<artifactId>hadoop-common</artifactId>-->
      <!--<version>2.6.0</version>-->
      <!--&lt;!&ndash;<scope>provided</scope>&ndash;&gt;-->
    <!--</dependency>-->
    <!--<dependency>-->
      <!--<groupId>org.apache.hadoop</groupId>-->
      <!--<artifactId>hadoop-hdfs</artifactId>-->
      <!--<version>2.6.0</version>-->
      <!--&lt;!&ndash;<scope>provided</scope>&ndash;&gt;-->
    <!--</dependency>-->
    <!--<dependency>-->
      <!--<groupId>org.apache.hadoop</groupId>-->
      <!--<artifactId>hadoop-client</artifactId>-->
      <!--<version>2.6.0</version>-->
      <!--&lt;!&ndash;<scope>provided</scope>&ndash;&gt;-->
    <!--</dependency>-->
    <!--<dependency>-->
      <!--<groupId>org.apache.hbase</groupId>-->
      <!--<artifactId>hbase-server</artifactId>-->
      <!--<version>1.2.6</version>-->
      <!--&lt;!&ndash;<scope>provided</scope>&ndash;&gt;-->
    <!--</dependency>-->
    <!--<dependency>-->
      <!--<groupId>org.apache.hbase</groupId>-->
      <!--<artifactId>hbase-client</artifactId>-->
      <!--<version>1.2.6</version>-->
      <!--&lt;!&ndash;<scope>provided</scope>&ndash;&gt;-->
    <!--</dependency>-->

    <!--<dependency>-->
      <!--<groupId>org.apache.spark</groupId>-->
      <!--<artifactId>spark-core_2.11</artifactId>-->
      <!--<version>${spark.version}</version>-->
      <!--&lt;!&ndash;<scope>provided</scope>&ndash;&gt;-->
    <!--</dependency>-->
    <!--<dependency>-->
      <!--<groupId>org.apache.spark</groupId>-->
      <!--<artifactId>spark-mllib_2.11</artifactId>-->
      <!--<version>${spark.version}</version>-->
      <!--&lt;!&ndash;<scope>provided</scope>&ndash;&gt;-->
    <!--</dependency>-->
    <!--<dependency>-->
      <!--<groupId>org.apache.spark</groupId>-->
      <!--<artifactId>spark-graphx_2.11</artifactId>-->
      <!--<version>${spark.version}</version>-->
      <!--&lt;!&ndash;<scope>provided</scope>&ndash;&gt;-->
    <!--</dependency>-->
    <!--<dependency>-->
      <!--<groupId>org.apache.spark</groupId>-->
      <!--<artifactId>spark-sql_2.11</artifactId>-->
      <!--<version>${spark.version}</version>-->
    <!--</dependency>-->
    <!--<dependency>-->
      <!--<groupId>org.apache.hadoop</groupId>-->
      <!--<artifactId>hadoop-client</artifactId>-->
      <!--<version>2.6.0</version>-->
      <!--&lt;!&ndash;<scope>provided</scope>&ndash;&gt;-->
    <!--</dependency>-->
    <!--<dependency>-->
      <!--<groupId>org.apache.spark</groupId>-->
      <!--<artifactId>spark-hive_2.11</artifactId>-->
      <!--<version>${spark.version}</version>-->
      <!--&lt;!&ndash;<scope>provided</scope>&ndash;&gt;-->
    <!--</dependency>-->
    <!--for local test jar -->



    <!--kafka streaming-->
    <dependency>
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-streaming_2.11</artifactId>
      <version>${spark.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
      <version>${spark.version}</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.53</version>
    </dependency>
    <!--kafka streaming-->
    <dependency>
      <groupId>cn.common</groupId>
      <artifactId>utils</artifactId>
      <version>1.0-SNAPSHOT</version>
      <exclusions>
        <exclusion>
          <groupId>org.slf4j</groupId>
          <artifactId>log4j-over-slf4j</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.apache.kafka</groupId>
          <artifactId>kafka_2.10</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <!--<dependency>-->
    <!--<groupId>com.xxx.i.gps</groupId>-->
    <!--<artifactId>i.gps</artifactId>-->
    <!--<version>1.0-SNAPSHOT</version>-->
    <!--</dependency>-->
    <!--<dependency>-->
    <!--<groupId>com.xxx.gis</groupId>-->
    <!--<artifactId>gis-tools</artifactId>-->
    <!--<version>1.1</version>-->
    <!--</dependency>-->
    <!--<dependency>-->
    <!--<groupId>org.geotools</groupId>-->
    <!--<artifactId>gt-main</artifactId>-->
    <!--<version>${geotools.version}</version>-->
    <!--</dependency>-->
    <!--<dependency>-->
    <!--<groupId>org.geotools</groupId>-->
    <!--<artifactId>gt-shapefile</artifactId>-->
    <!--<version>${geotools.version}</version>-->
    <!--</dependency>-->
    <!--开发的字节等转换库-->
    <!--<dependency>-->
    <!--<groupId>com.xxx.i.dbcommon</groupId>-->
    <!--<artifactId>i.dbcommon</artifactId>-->
    <!--<version>1.0-SNAPSHOT</version>-->
    <!--</dependency>-->
    <!-- https://mvnrepository.com/artifact/org.apache.spark/spark-hive_2.11 -->
    <!--时间 字节转化使用工具-->
    <dependency>
      <groupId>cn.common</groupId>
      <artifactId>utils</artifactId>
      <version>1.0-SNAPSHOT</version>
      <exclusions>
        <exclusion>
          <groupId>org.slf4j</groupId>
          <artifactId>log4j-over-slf4j</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.apache.kafka</groupId>
          <artifactId>kafka_2.10</artifactId>
        </exclusion>
      </exclusions>
      <!--<scope>provided</scope>-->
    </dependency>
    <!--gps 工具  24HSpeedDistribution需要-->
    <dependency>
      <groupId>com.xxx.i.gps</groupId>
      <artifactId>i.gps</artifactId>
      <version>1.0-SNAPSHOT</version>
      <!--<scope>provided</scope>-->
    </dependency>
    <dependency>
      <groupId>com.xxx.gis</groupId>
      <artifactId>gis-tools</artifactId>
      <version>1.1</version>
      <!--<scope>provided</scope>-->
    </dependency>
    <dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-main</artifactId>
      <version>${geotools.version}</version>
      <!--<scope>provided</scope>-->
    </dependency>
    <dependency>
      <groupId>org.geotools</groupId>
      <artifactId>gt-shapefile</artifactId>
      <version>${geotools.version}</version>
      <!--<scope>provided</scope>-->
    </dependency>
    <!--MongoDB-->
    <dependency>
    <groupId>org.mongodb.spark</groupId>
    <artifactId>mongo-spark-connector_2.11</artifactId>
      <version>${spark.version}</version>
  </dependency>
  <dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.6.3</version>
  </dependency>
  <dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>bson</artifactId>
    <version>3.4.0</version>
  </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
  </dependencies>


  <build>
    <finalName>xxx6_0</finalName>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
      <resource>
        <directory>src/main/config</directory>
        <!--<filtering>true</filtering>-->
      </resource>
    </resources>
    <!--<pluginManagement>&lt;!&ndash; lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) &ndash;&gt;-->
    <plugins>
      <!--<plugin>-->
      <!--<artifactId>maven-clean-plugin</artifactId>-->
      <!--<version>3.0.0</version>-->
      <!--</plugin>-->
      <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.0.2</version>
        <configuration>
          <encoding>${project.build.sourceEncoding}</encoding>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
          <source>${java.source}</source>
          <target>${java.target}</target>
          <encoding>${project.build.sourceEncoding}</encoding>
          <compilerArguments>
            <extdirs>lib/</extdirs>
          </compilerArguments>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
          <archive>
            <manifest>
              <mainClass>
                com.xxx.HiveToMongoDB
              </mainClass>
            </manifest>
          </archive>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <!--<plugin>-->
      <!--<artifactId>maven-surefire-plugin</artifactId>-->
      <!--<version>2.20.1</version>-->
      <!--</plugin>-->
      <!--<plugin>-->
      <!--<artifactId>maven-jar-plugin</artifactId>-->
      <!--<version>3.0.2</version>-->
      <!--</plugin>-->
      <!--<plugin>-->
      <!--<artifactId>maven-install-plugin</artifactId>-->
      <!--<version>2.5.2</version>-->
      <!--</plugin>-->
      <!--<plugin>-->
      <!--<artifactId>maven-deploy-plugin</artifactId>-->
      <!--<version>2.8.2</version>-->
      <!--</plugin>-->
    </plugins>
    <!--</pluginManagement>-->
  </build>

</project>

common

public class DataItem  {//

    public String name;

    public Records records;

//.....此处省略构造函数、以及get、set函数
}
public class Records {
    public int d1;

    public String d2;

    public int d3;

    public int d4;

    public double d5;
//......此处省略数据名、已经构造函数、set、get函数
}
HiveToMongoDB
package com.xxx

import com.mongodb
import com.mongodb.{MongoClient, MongoClientURI}
import com.mongodb.client.MongoCollection
import com.mongodb.spark.MongoSpark
import com.mongodb.spark.config.{WriteConcernConfig, WriteConfig}
import common.{DataItem, Records}
import org.apache.spark.sql.SparkSession
import org.bson.Document
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.query.{Criteria, Query, Update}

object HiveToMongoDB {
  def main(args: Array[String]): Unit = {
//打包到集群运行定时任务

    val excutors=args(0).toInt   //容器个数
    val cores=args(1).toInt   //核心个数
    val years = args(2)   //年   Hive数据表分区以及 mongoDB 的集合(collection)名称
    val months = args(3)  //月   Hive数据表分区
    val days = args(4)    //日   Hive数据表分区
    val mongoDBName = args(5)//mongoDB 名字
    val mongoDBKeyFieldName =args(6)//存于mongoDB的数据的Key的列名

    val url = s"mongodb://yonghuming:123456@10.9.43.217/"+mongoDBName+"."+years
//本地调试
//    val uri = s"mongodb://yonghuming:123456@10.9.43.217/mongo-test.2048"
//    val excutors=10
//    val cores=4
//    val years = "2020"
//    val months = "06"
//    val days = "01"


    //本地模式
//        System.setProperty("hadoop.home.dir", "D:\\D\\softs\\hadoop-2.8.1\\hadoop-common-2.2.0-bin-master")
    // warehouseLocation points to the default location for managed databases and tables
//    val warehouseLocation = "D:\\warehouse"
//    val spark = SparkSession.builder()
//      .appName("charge_pile")
//      //          .config("spark.sql.warehouse.dir", "C:/Users/tmp/hive")
//      .config("hadoop.home.dir", "D:\\D\\softs\\hadoop-2.8.1\\hadoop-2.8.1")
//      .config("spark.sql.warehouse.dir", warehouseLocation) //本地Hive使用调试
      .enableHiveSupport() //orc file must be hive support 本地Hive使用调试
//      .master("local[*]")
//      .getOrCreate()
    //集群调试
        val spark = SparkSession.builder()
          .appName("App_xxx_data_to_MongoDB")
  //The number of executors for static allocation. With spark.dynamicAllocation.enabled, the initial set of executors will be at least this large.
  .config("spark.dynamicAllocation.enabled",false)
  .config("spark.executor.instances", excutors) //excutor数量
          .config("spark.executor.cores", cores) //是指每个executor需要的核数
          .config("spark.default.parallelism", 10 * excutors * cores) //设置每个stage的默认task数量,num-executors * executor-cores的2~3倍较为合适
//          .config("spark.mongodb.output.uri",uri)
//          .config("spark.mongodb.input.uri",uri)
          .config("spark.some.config.option", "some-value")
          .enableHiveSupport()
          .getOrCreate()
    spark.sparkContext.setLogLevel("ERROR")
    import spark.implicits._
    val sql =
      """
        |SELECT name,d1,regexp_replace(d2,"/","") as ymd ,d3,d4,d5,d6,d7,d8,d9,d10,
        |d11,d12,d13,d14,d15,d16
        |from xxx_dwd.user_data_di
        |where years='%1$s' and months='%2$s' and days='%3$s'
      """.stripMargin.format(years,months,days)
    val df = spark.sql(sql)
    df.show()
    val broadCastDBName= spark.sparkContext.broadcast(mongoDBName)
    val broadCastUrl = spark.sparkContext.broadcast(url)
    val broadCastCollectionName = spark.sparkContext.broadcast(years)
    val broadCastKeyFieldName = spark.sparkContext.broadcast(mongoDBKeyFieldName)
     //方式三:Client方式文档内嵌新增更新插入
    //注:这种方式根据partition建立多个连接写mongoDB,导致同一个key数据没有保存在同一个文档下,需要改进:见方式四
    val rddDoc = df.map(e =>(e.getString(0),(e.getInt(1),e.getString(2),e.getInt(3),e.getInt(4),
    e.getDouble(5),e.getDouble(6),e.getDouble(7),e.getDouble(8),e.getDouble(9),
      e.getDouble(10),e.getDouble(11),e.getDouble(12),e.getDouble(13),e.getDouble(14),e.getDouble(15),e.getDouble(16))
    ))
//    .repartition(10)//设置partition个数为6,这个与MongoDB的连接数有关,连接数多时可以设置更大一点
//      .foreachPartition(e =>{
//      //val url = s"mongodb://dilink:123456@10.9.43.217/mongo-test.2048"
//      // 配置MongoClientURI
//      val urls:MongoClientURI = new MongoClientURI(broadCastUrl.value)
//      // 连接mongoclient
//      val mongoClient = new MongoClient(urls)
//      val mongoTemplate = new MongoTemplate(mongoClient,broadCastDBName.value)
//      val driveDataRecordsItem = e.foreach(t => {
//        val query = Query.query(Criteria.where(broadCastKeyFieldName.value).is(t._1))
//        val update = new Update()
//        val ddr = new DriveDataItem(t._1,new Records(t._2._1,t._2._2,t._2._3,t._2._4,t._2._5,t._2._6,t._2._7,t._2._8,t._2._9,t._2._10,t._2._11,t._2._12,t._2._13,t._2._14,t._2._15,t._2._16))
//        update.push("records", ddr.getRecords)
//        mongoTemplate.upsert(query, update, broadCastCollectionName.value)
//      })
//        mongoClient.close()//释放连接
//      })
        //方式四:改进方式三存在多个相同key的文档,所有RDD数据分组之后再插入
//      .groupByKey(e => e._1)
//        .mapGroups((k,v)=>{
//          //创建连接太多,导致失败
//          //          val url = s"mongodb://dilink:123456@10.9.43.217/mongo-test.2048"
//          // 配置MongoClientURI
//          val urls: MongoClientURI = new MongoClientURI(broadCastUrl.value)
//          // 连接mongoclient
//          val mongoClient = new MongoClient(urls)
//          val mongoTemplate = new MongoTemplate(mongoClient, broadCastDBName.value)
//          v.toList.foreach(t =>{
//            val query = Query.query(Criteria.where(broadCastKeyFieldName.value).is(t._1))
//            val update = new Update()
//            val ddr = new DriveDataItem(t._1, new Records(t._2._1, t._2._2, t._2._3, t._2._4, t._2._5, t._2._6, t._2._7, t._2._8, t._2._9, t._2._10, t._2._11, t._2._12, t._2._13, t._2._14, t._2._15, t._2._16))
//            update.push("records", ddr.getRecords)
//            mongoTemplate.upsert(query, update, broadCastCollectionName.value)
//          })
//          mongoClient.close() //释放连接
//          (k,1)
//        })
//    rddDoc.count()
        .groupByKey(key=>key._1)
        .mapGroups((k,v)=>(k,v.toList))//保证相同Key数据在一起
        .repartition(10)
        .foreachPartition(kvl=>{
          //          val url = s"mongodb://dilink:123456@10.9.43.217/mongo-test.2048"
          // 配置MongoClientURI
          val urls: MongoClientURI = new MongoClientURI(broadCastUrl.value)
          // 连接mongoclient
          val mongoClient = new MongoClient(urls)
          val mongoTemplate = new MongoTemplate(mongoClient, broadCastDBName.value)
          kvl.foreach(e =>{
            e._2.foreach(t=>{
              val query = Query.query(Criteria.where(broadCastKeyFieldName.value).is(t._1))
              val update = new Update()
              val ddr = new DriveDataItem(t._1, new Records(t._2._1, t._2._2, t._2._3, t._2._4, t._2._5, t._2._6, t._2._7, t._2._8, t._2._9, t._2._10, t._2._11, t._2._12, t._2._13, t._2._14, t._2._15, t._2._16))
              update.push("records", ddr.getRecords)
              mongoTemplate.upsert(query, update, broadCastCollectionName.value)
            })
          })
          mongoClient.close() //释放连接
        })


//    val wdf = df.toDF("name","d1","d2","d3","d4",
//      "d5","d6","d7","d8","d9",
//      "d10","d11","d12","d13","d14",
//      "d15","d16")
    //方式一手动配置WriteConfig
//    val writeConfig = new WriteConfig(databaseName="mongo-test",
//  collectionName="2048",
//  connectionString = Some(uri),
//    replaceDocument= false,
//    maxBatchSize= 512,
//    localThreshold= 15,
//    writeConcernConfig = WriteConcernConfig.Default,
//    shardKey= None,
//    forceInsert= true,
//    ordered= true)
//    MongoSpark.save(wdf,writeConfig)
    //方式二
//    wdf.write
//      .mode("append")
//      .format("com.mongodb.spark.sql.DefaultSource")
//      .options(
//        Map(
//        "uri" -> uri,
//        "database" -> "mongo-test",
//        "collection" -> "2048",
//        "updateFields" -> "name",
//        "forceInsert" -> "true",
//        "replaceDocument"->"true")
//      ).save()
    spark.stop()
  }
}

本地打包到CDH平台运行:

HiveToMongoDB.bat文件

mvn install & spark-submit --principal xxx.wangnengjie@xxx.COM --keytab xxx.xxx.keytab --class com.xxx.HiveToMongoDB --conf spark.sql.shuffle.partitions=400 --conf spark.default.parallelism=200 --conf spark.network.timeout=1200 --master yarn --deploy-mode client --driver-memory 7g --executor-memory 7g --executor-cores 4 --queue thequeue  target/xxx6_0-jar-with-dependencies.jar 5 4 2020 06 01 mongo-test name

CDH集群 Workflow的shell方式提交:

spark-submit --principal xxx.xx@XXX.COM \
    --keytab xxx.xx.keytab \
    --class com.xxx.HiveToMongoDB \
    --master yarn \
    --deploy-mode cluster \
    --driver-memory 7g \
    --executor-memory 7g \
    --queue thequeue \
    --conf spark.driver.extraClassPath=/etc/hbase/conf/ \
    --conf spark.sql.shuffle.partitions=400 \
    --conf spark.default.parallelism=200 \
    --conf spark.network.timeout=1200 \
    App6_0-jar-with-dependencies.jar \
    $1 $2 $3 $4 $5 $6 $7

CDH界面:

MongoDB相关插入新能问题:

 

upsert会遇到的问题   原文链接:https://blog.csdn.net/jeffrey11223/article/details/80366368

新服务已经上线跑了差不多两周,暴露出一个问题,我们发现db进行upsert的速度越来越慢,以前两个小时就能消费完队列里的数据,现在需要四五个小时,并且消耗时间是呈现不断上升的趋势。所以我觉得应该是和upsert这个操作有关。

问题定位:
1,由于是写多读少的场景,所以我们并没有对集合加入索引。并且经查阅资料发现,mongodb索引的存储机制和mysql不同,mysql的索引是存储在硬盘中,需要时会调用部分到内存中。而mongodb的索引则是直接存储在内存和临时文件中,并且和内存大小限制有很直接的关系,如果超过内存限制,则从硬盘加载索引。所以mongodb索引的使用,在大数据集合面前,会面临内存耗尽的风险。

下面这个链接是官方对索引使用限制的说明:
https://docs.mongodb.com/manual/reference/limits/#index-limitations


这里写图片描述

2,upsert操作会先在集合中进行数据查找,如果数据已经存在,则更新,否则才插入。数据的查找那就势必会使用索引,mongo索引用的是B树,时间复杂度为Olog(n),而没有索引的情况下则时间复杂度是O(n),差别见下图:
这里写图片描述

问题显而易见了,随着数据日益增多,upsert性能是线性下滑的,所以后来决定使用insert,因为确实队列中重复数据的概率是比较小的,为了这个概率而损失性能和时间,划不来。

Mongodb亿级数据量的性能测试  原文链接:https://blog.csdn.net/zrjdds/article/details/52159936

进行了一下Mongodb亿级数据量的性能测试,分别测试如下几个项目:

(所有插入都是单线程进行,所有读取都是多线程进行)

1) 普通插入性能 (插入的数据每条大约在1KB左右)

2) 批量插入性能 (使用的是官方C#客户端的InsertBatch),这个测的是批量插入性能能有多少提高

3) 安全插入功能 (确保插入成功,使用的是SafeMode.True开关),这个测的是安全插入性能会差多少

4) 查询一个索引后的数字列,返回10条记录(也就是10KB)的性能,这个测的是索引查询的性能

5) 查询两个索引后的数字列,返回10条记录(每条记录只返回20字节左右的2个小字段)的性能,这个测的是返回小数据量以及多一个查询条件对性能的影响

6) 查询一个索引后的数字列,按照另一个索引的日期字段排序(索引建立的时候是倒序,排序也是倒序),并且Skip100条记录后返回10条记录的性能,这个测的是Skip和Order对性能的影响

7) 查询100条记录(也就是100KB)的性能(没有排序,没有条件),这个测的是大数据量的查询结果对性能的影响

8) 统计随着测试的进行,总磁盘占用,索引磁盘占用以及数据磁盘占用的数量

并且每一种测试都使用单进程的Mongodb和同一台服务器开三个Mongodb进程作为Sharding(每一个进程大概只能用7GB左右的内存)两种方案

其实对于Sharding,虽然是一台机器放3个进程,但是在查询的时候每一个并行进程查询部分数据,再有运行于另外一个机器的mongos来汇总数据,理论上来说在某些情况下性能会有点提高

基于以上的种种假设,猜测某些情况性能会下降,某些情况性能会提高,那么来看一下最后的测试结果怎么样?

备注:测试的存储服务器是 E5620  @ 2.40GHz,24GB内存,CentOs操作系统,打压机器是E5504 @ 2.0GHz,4GB内存,Windows Server 2003操作系统,两者千兆网卡直连。

image

从这个测试可以看出,对于单进程的方式:

1) Mongodb的非安全插入方式,在一开始插入性能是非常高的,但是在达到了两千万条数据之后性能骤减,这个时候恰巧是服务器24G内存基本占满的时候(随着测试的进行mongodb不断占据内存,一直到操作系统的内存全部占满),也就是说Mongodb的内存映射方式,使得数据全部在内存中的时候速度飞快,当部分数据需要换出到磁盘上之后,性能下降很厉害。(这个性能其实也不算太差,因为我们对三个列的数据做了索引,即使在内存满了之后每秒也能插入2MB的数据,在一开始更是每秒插入25MB数据)。Foursquare其实也是把Mongodb当作带持久化的内存数据库使用的,只是在查不到达到内存瓶颈的时候Sharding没处理好。

2) 对于批量插入功能,其实是一次提交一批数据,但是相比一次一条插入性能并没有提高多少,一来是因为网络带宽已经成为了瓶颈,二来我想写锁也会是一个原因。

3) 对于安全插入功能,相对来说比较稳定,不会波动很大,我想可能是因为安全插入是确保数据直接持久化到磁盘的,而不是插入内存就完事。

4) 对于一列条件的查询,性能一直比较稳定,别小看,每秒能有8000-9000的查询次数,每次返回10KB,相当于每秒查询80MB数据,而且数据库记录是2亿之后还能维持这个水平,性能惊人。

5) 对于二列条件返回小数据的查询,总体上性能会比4)好一点,可能返回的数据量小对性能提高比较大,但是相对来说性能波动也厉害一点,可能多了一个条件就多了一个从磁盘换页的机会。

6) 对于一列数据外加Sort和Skip的查询,在数据量大了之后性能明显就变差了(此时是索引数据量超过内存大小的时候,不知道是否有联系),我猜想是Skip比较消耗性能,不过和4)相比性能也不是差距特别大。

7) 对于返回大数据的查询,一秒瓶颈也有800次左右,也就是80M数据,这就进一步说明了在有索引的情况下,顺序查询和按条件搜索性能是相差无几的,这个时候是IO和网络的瓶颈。

8) 在整个过程中索引占的数据量已经占到了总数据量的相当大比例,在达到1亿4千万数据量的时候,光索引就可以占据整个内存,此时查询性能还是非常高,插入性能也不算太差,mongodb的性能确实很牛。

那么在来看看Sharding模式有什么亮点:

1) 非安全插入和单进程的配置一样,在内存满了之后性能急剧下降。安全插入性能和单进程相比慢不少,但是非常稳定。

2) 对于一个条件和两个条件的查询,性能都比较稳定,但条件查询性能相当于单进程的一半,但是在多条件下有的时候甚至会比单进程高一点。我想这可能是某些时候数据块位于两个Sharding,这样Mongos会并行在两个Sharding查询,然后在把数据进行合并汇总,由于查询返回的数据量小,网络不太可能成为瓶颈了,使得Sharding才有出头的机会。

3) 对于Order和Skip的查询,Sharding方式的差距就出来了,我想主要性能损失可能在Order,因为我们并没有按照排序字段作为Sharding的Key,使用的是_id作为Key,这样排序就比较难进行。

4) 对于返回大数据量的查询,Sharding方式其实和单进程差距不是很大,我想数据的转发可能是一个性能损耗的原因(虽然mongos位于打压机本机,但是数据始终是转手了一次)。

5) 对于磁盘空间的占用,两者其实是差不多的,其中的一些差距可能是因为多个进程都会多分配一点空间,加起来有的时候会比单进程多占用点磁盘(而那些占用比单进程少的地方其实是开始的编码错误,把实际数据大小和磁盘文件占用大小搞错了)。

测试最后的各个Sharding分布情况如下:

{
        "sharded" : true,
        "ns" : "testdb.test",
        "count" : 209766143,
        "size" : 214800530672,
        "avgObjSize" : 1024.0000011441311,
        "storageSize" : 222462757776,
        "nindexes" : 4,
        "nchunks" : 823,
        "shards" : {
                "shard0000" : {
                        "ns" : "testdb.test",
                        "count" : 69474248,
                        "size" : 71141630032,
                        "avgObjSize" : 1024.0000011515058,
                        "storageSize" : 74154252592,
                        "numExtents" : 65,
                        "nindexes" : 4,
                        "lastExtentSize" : 2146426864,
                        "paddingFactor" : 1,
                        "flags" : 1,
                        "totalIndexSize" : 11294125824,
                        "indexSizes" : {
                                "_id_" : 2928157632,
                                "Number_1" : 2832745408,
                                "Number1_1" : 2833974208,
                                "Date_-1" : 2699248576
                        },
                        "ok" : 1
                },
                "shard0001" : {
                        "ns" : "testdb.test",
                        "count" : 70446092,
                        "size" : 72136798288,
                        "avgObjSize" : 1024.00000113562,
                        "storageSize" : 74154252592,
                        "numExtents" : 65,
                        "nindexes" : 4,
                        "lastExtentSize" : 2146426864,
                        "paddingFactor" : 1,
                        "flags" : 1,
                        "totalIndexSize" : 11394068224,
                        "indexSizes" : {
                                "_id_" : 2969355200,
                                "Number_1" : 2826453952,
                                "Number1_1" : 2828403648,
                                "Date_-1" : 2769855424
                        },
                        "ok" : 1
                },
                "shard0002" : {
                        "ns" : "testdb.test",
                        "count" : 69845803,
                        "size" : 71522102352,
                        "avgObjSize" : 1024.00000114538,
                        "storageSize" : 74154252592,
                        "numExtents" : 65,
                        "nindexes" : 4,
                        "lastExtentSize" : 2146426864,
                        "paddingFactor" : 1,
                        "flags" : 1,
                        "totalIndexSize" : 11300515584,
                        "indexSizes" : {
                                "_id_" : 2930942912,
                                "Number_1" : 2835243968,
                                "Number1_1" : 2835907520,
                                "Date_-1" : 2698421184
                        },
                        "ok" : 1
                }
        },
        "ok" : 1
}

虽然在最后由于时间的关系,没有测到10亿级别的数据量,但是通过这些数据已经可以证明Mongodb的性能是多么强劲了。另外一个原因是,在很多时候可能数据只达到千万我们就会对库进行拆分,不会让一个库的索引非常庞大。在测试的过程中还发现几个问题需要值得注意:

1) 在数据量很大的情况下,对服务进行重启,那么服务启动的初始化阶段,虽然可以接受数据的查询和修改,但是此时性能很差,因为mongodb会不断把数据从磁盘换入内存,此时的IO压力非常大。

2) 在数据量很大的情况下,如果服务没有正常关闭,那么Mongodb启动修复数据库的时间非常可观,在1.8中退出的-dur貌似可以解决这个问题,据官方说对读取没影响,写入速度会稍稍降低,有空我也会再进行下测试。

3) 在使用Sharding的时候,Mongodb时不时会对数据拆分搬迁,这个时候性能下降很厉害,虽然从测试图中看不出(因为我每一次测试都会测试比较多的迭代次数),但是我在实际观察中可以发现,在搬迁数据的时候每秒插入性能可能会低到几百条。其实我觉得能手动切分数据库就手动切分或者手动做历史库,不要依赖这种自动化的Sharding,因为一开始数据就放到正确的位置比分隔再搬迁效率不知道高多少。个人认为Mongodb单数据库存储不超过1亿的数据比较合适,再大还是手动分库吧。

4) 对于数据的插入,如果使用多线程并不会带来性能的提高,反而还会下降一点性能(并且可以在http接口上看到,有大量的线程处于等待)。

5) 在整个测试过程中,批量插入的时候遇到过几次连接被远程计算机关闭的错误,怀疑是有的时候Mongodb不稳定关闭了连接,或是官方的C#客户端有BUG,但是也仅仅是在数据量特别大的时候遇到几次。

最新补充:在之后我又进行了几天测试,把测试数据量进一步加大到5亿,总磁盘占用超过500G,发现和2亿数据量相比,所有性能都差不多,只是测试6和测试7在超过2亿级别数据之后,每400万记录作为一个循环,上下波动30%的性能,非常有规律。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值