如何在Pyspark中处理具有许多可选字段(动态架构)的大型mongo集合

时间:2019-04-17 21:00:33

标签: json mongodb pyspark

我正在处理一个Mongo集合,其中包含数千个元素,所有元素都是可选的。元素是可选的,因为它们反映了每个ID可用的源数据。我还可能要补充一点,到目前为止,根据我的分析,在集合的某些部分中,我不得不猜测出嵌套结构和数组的多个级别。

我目前遇到的问题是架构定义是动态的。 mongo连接器文档仅显示pyspark示例,该示例在一个数据帧中使用该连接器中的数据,在该数据帧中会推断出提供了用于将数据写入hdfs的sql接口的模式。假定由于使用数据帧内置的优化而说明了示例。但是,这种方法中内置的模式推断似乎并不像在许多字段为可选字段时对足够的行进行采样,因此我的作业崩溃,因为执行爆炸操作时嵌套对象中的类型不匹配。

环境配置:

mongo-spark-connector_2.11-2.1.5.jar

mongodb-driver-3.6.4.jar

mongodb-driver-core-3.6.4.jar

bson-3.6.4.jar

Python 3.3.2

火花2.1.0

MongoDB服务器版本:3.4.19

http://spark.apache.org/docs/2.1.0/api/python/

https://docs.mongodb.com/spark-connector/master/python-api/

我们开会讨论方法,我愿意接受执行完整mongo扫描以确定超集架构的性能开销。

因此,我尝试转换从mongo连接器示例返回的数据框:

someDF = spark.read.format(“ com.mongodb.spark.sql.DefaultSource”)。load()

进入一个RDD,在这里我可以利用pyspark API函数createDataFrame。从df到rdd再回到rdd的原因是,我希望覆盖创建原始df时使用的模式推断,在此之前,我可以显式采样较大部分的人口,以尝试获取推断的模式。

someNewDF = spark.createDataFrame(someDF.rdd,schema = None,samplerRatio = 1)

什么不起作用-我相信someNewDF返回无类型。这与转换为RDD有关吗?至少尝试在json_root上执行节目会引发“ nonetype”不可迭代的异常

import os
import sys
import platform

from pyspark.conf import SparkConf
from pyspark.context import SparkContext
from pyspark.sql import SparkSession
from pyspark.sql import functions as psf
from pyspark.sql.types import *
from py4j.protocol import Py4JError

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print >> sys.stderr, "Usage: myScript.py config.properties"
        sys.exit(-1)

arguments = sys.argv[1:]
print("Starting "+str(sys.argv[0])+" with "+str(sys.argv[1]))

conf = SparkConf()
conf.set("spark.mongodb.input.uri", "mongodb://hostname:27017/some_database.some_collection")
conf.set("spark.debug.maxToStringFields", 2048)

# Create a new SparkContext used for RDD's
sc = SparkContext("yarn")

# Set log level to ERROR to avoid distracting extra output ** changed to trace temporarily to capture more data
sc.setLogLevel("TRACE")

# Create a new SparkSession used for DataFrames
spark = SparkSession.builder \
                    .master("yarn") \
                    .appName(str(sys.argv[0])) \
                    .config(conf=conf) \
                    .getOrCreate()


someDF = spark.read.format("com.mongodb.spark.sql.DefaultSource").load()

**newDF = spark.createDataFrame(someDF.rdd, schema=None, samplingRatio=1)**

json_root = newDF.select(psf.reverse(newDF._id).alias("id"), \
            newDF.audit.modDate.alias("audit_modDate"), \                               
            newDF.audit.processedDate.alias("audit_processedDate"), \
            newDF.patch.alias("patch"), \                           
            newDF.report.acJurisdictions.alias("acJurisdictions"), \                                 
            newDF.report.cachemd5.alias("cachemd5"), \                               
            newDF.report.decode.baseLPrice.alias("baseLPrice"), \
            newDF.report.decode.baseMod.alias("baseMod"), \
           newDF.report.decode.baseShippingWeight.alias("baseShippingWeight"), \           
            newDF.report.decode.bodyTypeCode.alias("bodyTypeCode"), \
            psf.explode(newDF.some_col))

json_root.show()

# There are multiple steps of dataframe transformations after this where I explode out structs with * wildcard and explode more arrays (** you can only explode one array per dataframe unless you use the spark sql query interface). When the data is ready to be written to the parquet file in the final step is when evaluation of all the dataframes this is where the schema collision occurs with the inferred schema                     

##...more data frame operations to flatten nested structures here

json_lvl4.show()

json_lvl4.createOrReplaceTempView("vRecDF_tab")
spark.sql("DROP TABLE IF EXISTS database.my_table")
spark.sql("""
CREATE TABLE database.my_table
STORED AS PARQUET
as
SELECT * FROM vRecDF_tab
""")

部分有效,但迫使我进行了上述尝试- 返回到仅使用load语句,然后在数据框上显示 操作产生数据。如果我导出数据成功导出到表 json_root到json_lvl3。但是,在结构上使用*运算符将数据写入表时,尝试以嵌套类型进一步扩展结构会导致类型不匹配。这纯粹是我通过实验得出的推测,但是由于推断,这种扩展似乎在连续运行中产生了不同的表定义 内置到spark.read.format(“ com.mongodb.spark.sql.DefaultSource”)。load()中的架构样本大小 当文档中的可选字段过多时。

import os
import sys
import platform

from pyspark.conf import SparkConf
from pyspark.context import SparkContext
from pyspark.sql import SparkSession
from pyspark.sql import functions as psf
from pyspark.sql.types import *
from py4j.protocol import Py4JError

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print >> sys.stderr, "Usage: myScript.py config.properties"
        sys.exit(-1)

arguments = sys.argv[1:]
print("Starting "+str(sys.argv[0])+" with "+str(sys.argv[1]))

conf = SparkConf()
conf.set("spark.mongodb.input.uri", "mongodb://hostname:27017/some_database.some_collection")
conf.set("spark.debug.maxToStringFields", 2048)

# Create a new SparkContext used for RDD's
sc = SparkContext("yarn")

# Set log level to ERROR to avoid distracting extra output ** changed to trace temporarily to capture more data
sc.setLogLevel("TRACE")

# Create a new SparkSession used for DataFrames
spark = SparkSession.builder \
                    .master("yarn") \
                    .appName(str(sys.argv[0])) \
                    .config(conf=conf) \
                    .getOrCreate()


newDF = spark.read.format("com.mongodb.spark.sql.DefaultSource").load()

json_root = newDF.select(psf.reverse(newDF._id).alias("id"), \
            newDF.audit.modDate.alias("audit_modDate"), \                               
            newDF.audit.processedDate.alias("audit_processedDate"), \
            newDF.patch.alias("patch"), \                           
            newDF.report.acJurisdictions.alias("acJurisdictions"), \                                 
            newDF.report.cachemd5.alias("cachemd5"), \                               
            newDF.report.decode.baseLPrice.alias("baseLPrice"), \
            newDF.report.decode.baseMod.alias("baseMod"), \
           newDF.report.decode.baseShippingWeight.alias("baseShippingWeight"), \           
            newDF.report.decode.bodyTypeCode.alias("bodyTypeCode"), \
            psf.explode(newDF.some_col))

json_root.show()

##...more data frame operations to flatten nested structures here

json_lvl4.show()

json_lvl4.createOrReplaceTempView("vRecDF_tab")
spark.sql("DROP TABLE IF EXISTS database.my_table")
spark.sql("""
CREATE TABLE database.my_table
STORED AS PARQUET
as
SELECT * FROM vRecDF_tab
""")

由于多种原因,我很难提供代码示例: 1.我有数据隐私义务和NDA。 2.复制问题所需的代码相当大。

当我之前说过有很多可选字段时,底层json文档大约有113k个唯一字段。我猜想在mongo分片中存储的50亿条记录中,约有75%的字段覆盖了每个id。

要进一步增加代码段的复杂性:psf.explode(newDF.some_col))是其中之一 array<struct<struct,struct,struct,array<struct>.......

多次执行打印模式操作会对这些嵌套对象产生不同的定义,因为如前所述,默认模式推断没有对足够的文档进行采样。

文档非结构化格式的变化非常普遍,以至于我无法针对10万记录样本编写通用解决方案。这里要注意的重要一点是,特别是硬编码元素选择,其中已知元素覆盖了整个数据集,因此我能够成功地将拼花文件中的数据保存到HDFS中。当我开始发生架构冲突时,我便开始在文档中扩展嵌套类型。

在存在如此复杂的数据集时,是否有更好的方法来误解我?

是否有可能加载一个json库并将mongo记录推送到rdd中,然后将它们映射到python集合中并重组数据以使其更易于消化?

任何想法都很棒!请帮忙!

0 个答案:

没有答案