一、傳統的較為簡單的JDBC方式讀取
Spark版本:2.4 CDH
MongoDB Spark Connector github地址:
Maven倉庫
<dependency>
<groupId>org.mongodb.spark</groupId>
<artifactId>mongo-spark-connector_2.11</artifactId>
<version>2.4.1</version>
</dependency>
1.Java API
// 構建資料結構
// 根據實際的業務結構調整
// 建議提前組裝好結構
StructType arrObjectStruct = new StructType()
.add("xxxx", DataTypes.StringType)
.add("yyyy", DataTypes.StringType)
.add("zzzz", DataTypes.IntegerType)
;
StructType sourceSchema = new StructType()
.add("_class", DataTypes.StringType)
.add("_id", DataTypes.StringType)
.add("aaa", DataTypes.LongType)
.add("bbb", DataTypes.StringType)
.add("ccc", DataTypes.LongType)
.add("ddd", DataTypes.createArrayType(arrObjectStruct, true))
.add("eee", DataTypes.LongType)
.add("fff", DataTypes.LongType)
.add("ggg", DataTypes.LongType);
sparkSession.read().schema(sourceSchema)
.format("com.mongodb.spark.sql")
// MongoDB集合的JDBC連結
.option("spark.mongodb.input.uri", "mongodb://)
// 優化選項每批次讀取的記錄數,預設1000
.option("spark.mongodb.input.batchSize", 1000)
// 這裡定義的是分片實現,後面會介紹
.option("spark.mongodb.input.partitioner", "MongoPaginateByCountPartitioner")
// 預設引數,分片的主鍵
.option("spark.mongodb.input.partitionerOptions.partitionKey", "_id")
// 預設引數,分片數量。 最後生成的executor數量
.option("spark.mongodb.input.partitionerOptions.numberOfPartitions", 32)
.load().createTempView("tmp_sxxx_xxxx");
// 這時已經生成了臨時表
// 接下來就可以像寫普通SQL一樣處理資料了。
// 預設是開啟 push down filter 優化的。同時開啟null值過濾優化
// 因此進行SQL的增量查詢時,where條件會命中索引。
大資料培訓
// 如果記錄中有list需要一條變多條則需要使用mapPartitions運算元進行處理
JavaRDD<Row> javaRdd = sparkSession.sql(sql).javaRDD().mapPartitions(iterator -> {undefined
List<Row> result = Lists.newArrayList();
while (iterator.hasNext()) {undefined
Row row = iterator.next();
Long a = row.get(0) == null ? -99L : row.getLong(0);
Long b = row.get(1) == null ? -99L : row.getLong(1);
Long c = row.get(3) == null ? -99L : row.getLong(3);
Long d = row.get(4) == null ? -99L : row.getLong(4);
Long e = row.get(5) == null ? -99L : row.getLong(5);
Seq<Row> xx = row.get(2) == null ? null : row.getAs(2);
if (null != lectureRecords) {undefined
for (Row x : scala.collection.JavaConversions.seqAsJavaList(xx)) {undefined
String f = x.get(0) == null ? "" : x.getString(0);
String g = x.get(1) == null ? "" : x.getString(1);
Integer h = x.get(2) == null ? -99 : x.getInt(2);
result.add(RowFactory
.create(a,b,c,d,e,f,g,h));
}
}
}
return result.listIterator();
});
sparkSession.createDataFrame(javaRdd, getSchema())
.write().format("hive").mode(SaveMode.Overwrite).saveAsTable("test.test_mo");
2. 分割槽實現及限制
對於MongoDB版本的小於3.2的需要顯示的指定
如下三個引數
* Setting a "spark.mongodb.input.partitioner" in SparkConf.
* Setting in the "partitioner" parameter in ReadConfig.
* Passing the "partitioner" option to the DataFrameReader.
幾種分割槽的實現:
1. MongoShardedPartitioner
2. MongoSplitVectorPartitioner
3. MongoPaginateByCountPartitioner
4. MongoPaginateBySizePartitioner
具體的解釋可以檢視。com.mongodb.spark.rdd.partitioner.DefaultMongoPartitioner 原碼中的註釋
需要根據MongoDB的叢集部署方式選擇最適合自己的
每種分割槽實現需要的引數不盡相同。可以在對應的實現內檢視
二、自定義分割槽實現
Spark讀取MongoDB資料分割槽主要是通過繼承 MongoPartitioner 特質來實現partitions方法
ScalaAPI實現
1. 實現MongoPartitioner特質
// 可以自定義需要接受的引數,進行特定的處理
class MongoPartitionTest1 extends MongoPartitioner {undefined
private implicit object BsonValueOrdering extends BsonValueOrdering
private val DefaultPartitionKey = "_id"
private val DefaultNumberOfPartitions = "64"
override def partitions(connector: MongoConnector, readConfig: ReadConfig, pipeline: Array[BsonDocument]): Array[MongoPartition] = {undefined
// 自定義分割槽實現,可以參考原始碼中其他的分割槽實現
val boundaryQuery = PartitionerHelper.createBoundaryQuery("_id", new BsonObjectId(new ObjectId("5fedf5800000000000000000")), new BsonObjectId(new ObjectId("5fee03900000000000000000")))
val mongoPartition = new MongoPartition(0, boundaryQuery, PartitionerHelper.locations(connector))
Array[MongoPartition](mongoPartition)
}
}
case object MongoPartitionTest1 extends MongoPartitionTest1
2.讀取內容
val mongoReadConfig = new ReadConfig(
databaseName = "庫名",
collectionName = "集合名",
connectionString = None,
// 取樣大小
sampleSize = 1000,
// 自定義的分割槽實現
partitioner = MongoPartitionTest1,
// 自定義實現接收的引數
partitionerOptions = Map(),
// partitionerOptions = Map("spark.mongodb.input.partitionerOptions.partitionKey" -> "_id", "spark.mongodb.input.partitionerOptions.numberOfPartitions" -> "32"),
// 本地閥值
localThreshold = 15,
readPreferenceConfig = new ReadPreferenceConfig(),
readConcernConfig = new ReadConcernConfig(),
aggregationConfig = AggregationConfig(),
registerSQLHelperFunctions = false,
inferSchemaMapTypesEnabled = true,
inferSchemaMapTypesMinimumKeys = 250,
// null值過濾
pipelineIncludeNullFilters = true,
// push down filter
pipelineIncludeFiltersAndProjections = true,
// 樣本池大小
samplePoolSize = 1000,
//批次數量
batchSize = Option(1000)
)
val mongoConnector = MongoConnector(Map(
"spark.mongodb.input.uri" -> "mongodb://
"spark.mongodb.input.localThreshold" -> "15"))
val mongoSpark = MongoSpark.builder()
.sparkSession(sparkSession)
.readConfig(mongoReadConfig)
.connector(mongoConnector)
.build()
mongoSpark.toDF().schema(schema).where("everyDate = '2021-01-01' ").show(100, false)
3.注意事項
1.如果使用JDBC的方式,是使用反射的方式實現的。需要將自己實現的類以Jar包的方式上傳到Spark的classPath下。
2. 使用上述方式則不用進行特殊處理。
版權宣告:本文為原創文章,轉載請附上原文出處連結及本宣告。下載相關視訊學習資料到尚矽谷官方網站。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/27721058/viewspace-2852041/,如需轉載,請註明出處,否則將追究法律責任。