我正在使用Spark 1.6.2,Scala 2.10.5和Java 1.7。
我们的用例需要我们在超过2亿行的数据集上执行dense_rank()而不使用partitionBy子句,只使用orderBy子句。这当前在MSSQL中运行,大约需要30分钟才能完成。
我在Spark中实现了等效逻辑,如下所示:
val df1 = hqlContext.read.format("jdbc").options(
Map("url" -> url, "driver" -> driver,
"dbtable" -> "(select * from OwnershipStandardization_PositionSequence_tbl) as ps")).load()
df1.cache()
val df1_drnk = df1.withColumn("standardizationId",denseRank().over(Window.orderBy("ownerObjectId","securityId","periodId")))
我以Yarn-cluster模式提交作业,如下所示。我有一个2节点Hadoop 2.6集群,每个集群有4个vCore和32 GB内存。
spark-submit --class com.spgmi.csd.OshpStdCarryOver --master yarn --deploy-mode cluster --conf spark.yarn.executor.memoryOverhead=3072 --num-executors 2 --executor-cores 3 --driver-memory 7g --executor-memory 16g --jars $SPARK_HOME/lib/datanucleus-api-jdo-3.2.6.jar,$SPARK_HOME/lib/datanucleus-core-3.2.10.jar,$SPARK_HOME/lib/datanucleus-rdbms-3.2.9.jar,/usr/share/java/sqljdbc_4.1/enu/sqljdbc41.jar --files $SPARK_HOME/conf/hive-site.xml $SPARK_HOME/lib/spark-poc2-14.0.0.jar
在日志中,我可以看到来自MSSQL的大约200密耳行的表格正在导入&在15分钟内在Spark中缓存。我看到大约5 GB的内存在这个阶段被使用,其中一个执行程序仍有大约6.2 GB的内存,而另一个执行器上有11 GB的内存空闲。
但是,在dense_rank()的步骤总是失败,并且#34;超出了GC开销限制"几分钟后出错。我甚至将驱动程序内存设置为高达7g,您可以在上面的spark-submit命令中注意到。但是,无济于事! 当然,据我所知,缺少partitionBy子句实际上是在Spark中造成了麻烦。但不幸的是,这是我们需要处理的用例。
请你在这里说明一些事情吗?我错过了什么吗?有没有在Spark中使用dense_rank窗口函数的替代方法?例如,使用" zipWithIndex"本论坛其他地方的专家之一建议的功能?它会产生与dense_rank相同的结果,因为我理解" zipWithIndex"复制row_number()函数而不是dense_rank?
任何有用的建议表示赞赏! 非常感谢!
答案 0 :(得分:3)
这里有两个不同的问题:
您可以通过JDBC连接加载数据,而无需提供分区列或分区谓词。这使用单个执行程序线程加载所有数据。
通过使用现有列之一或提供人工密钥,这个问题通常很容易解决。
您使用没有partitionBy
的窗口函数。因此,所有数据都会重新整理到单个分区,在本地排序,并使用单个线程进行处理。
一般情况下,没有通用解决方案只能使用Dataset
API来解决这个问题,但您可以使用一些技巧:
创建反映所需记录顺序的人工分区。我在回答Avoid performance impact of a single partition mode in Spark window functions
时描述了这种方法在您的情况下可以使用类似的方法,但它需要多步骤过程,相当于下面描述的过程。
使用关联方法,您可以对已排序的RDD
进行两次单独扫描(应该可以执行类似的操作而不转换为Dataset
)和其他操作:
这种方法的一个例子,可以轻松调整以适合您的情况,可以在How to compute cumulative sum using Spark