如何让pyspark显示整个查询计划而不是......如果有很多字段?

Posted

技术标签:

【中文标题】如何让pyspark显示整个查询计划而不是......如果有很多字段?【英文标题】:How to let pyspark display the whole query plan instead of ... if there are many fields? 【发布时间】:2019-03-20 13:55:28 【问题描述】:

Spark v2.4

spark = SparkSession \
    .builder \
    .master('local[15]') \
    .appName('Notebook') \
    .config('spark.sql.debug.maxToStringFields', 2000) \
    .config('spark.sql.maxPlanStringLength', 2000) \
    .config('spark.debug.maxToStringFields', 2000) \
    .getOrCreate()

df = spark.createDataFrame(spark.range(1000).rdd.map(lambda x: range(100)))
df.repartition(1).write.mode('overwrite').parquet('test.parquet')

df = spark.read.parquet('test.parquet')
df.select('*').explain()

== Physical Plan ==

 ReadSchema: struct<_1:bigint,_2:bigint,_3:bigint,_4:bigint,_5:bigint,_6:bigint,_7:bigint,_8:bigint,_9:bigint,...

注意:spark.debug.maxToStringFields 通过扩展 FileScan parquet [_1#302L,_2#303L,... 76 more fields] 有所帮助,但不是架构部分。

注意2:我不仅对ReadSchema感兴趣,而且对PartitionFiltersPushedFilters ...都感兴趣。

更新

Spark 3.0 引入了explain('formatted'),它以不同的方式布局信息并且不应用截断。

【问题讨论】:

Spark: "Truncated the string representation of a plan since it was too large." Warning when using manually created aggregation expression的可能重复 你在哪里运行这段代码? @user10938362 我更新了问题,因为spark.debug.maxToStringFields 在这种情况下没有帮助。 @eliasah submit 和 python repl 为什么不使用 df.printSchema() 或 df.schema.json() ?我不确定我是否理解为什么这是一个问题... 【参考方案1】:

恐怕没有捷径

https://github.com/apache/spark/blob/v2.4.2/sql/core/src/main/scala/org/apache/spark/sql/execution/DataSourceScanExec.scala#L57

硬编码为不超过 100 个字符

  override def simpleString: String = 
    val metadataEntries = metadata.toSeq.sorted.map 
      case (key, value) =>
        key + ": " + StringUtils.abbreviate(redact(value), 100)
    

最后我一直在用

def full_file_meta(f: FileSourceScanExec) = 
    val metadataEntries = f.metadata.toSeq.sorted.flatMap 
      case (key, value) if Set(
          "Location", "PartitionCount",
          "PartitionFilters", "PushedFilters"
      ).contains(key) =>
        Some(key + ": " + value.toString)
      case other => None
    

    val metadataStr = metadataEntries.mkString("[\n  ", ",\n  ", "\n]")
    s"$f.nodeNamePrefix$f.nodeName$metadataStr"



val ep = data.queryExecution.executedPlan

print(ep.flatMap 
    case f: FileSourceScanExec => full_file_meta(f)::Nil
    case other => Nil
.mkString(",\n"))

这是一个 hack,总比没有好。

【讨论】:

@ colinfang 看到我的答案 - Spark 有一个远离simpleString 的内置支点可用,无需破解。 非常好的解决方案,如果没有这个 hack,几乎不可能检查谓词是否正确下推到文件扫描器。【参考方案2】:

Spark 3.0 引入了explain('formatted'),它以不同的方式布局信息并且不应用截断。

【讨论】:

【参考方案3】:

在 PySpark 中,你所要做的就是用扩展模式解释:

df.select('*').explain(True)

这会调用非simpleString 实现 - 请参阅the DataFrame#explain docs 了解更多信息。


运行问题中的代码会导致以下输出为df.select('*').explain(extended=True)

== Parsed Logical Plan ==
'Project [*]
+- Relation[_1#304L,_2#305L,_3#306L,_4#307L,_5#308L,_6#309L,_7#310L,_8#311L,_9#312L,_10#313L,_11#314L,_12#315L,_13#316L,_14#317L,_15#318L,_16#319L,_17#320L,_18#321L,_19#322L,_20#323L,_21#324L,_22#325L,_23#326L,_24#327L,_25#328L,_26#329L,_27#330L,_28#331L,_29#332L,_30#333L,_31#334L,_32#335L,_33#336L,_34#337L,_35#338L,_36#339L,_37#340L,_38#341L,_39#342L,_40#343L,_41#344L,_42#345L,_43#346L,_44#347L,_45#348L,_46#349L,_47#350L,_48#351L,_49#352L,_50#353L,_51#354L,_52#355L,_53#356L,_54#357L,_55#358L,_56#359L,_57#360L,_58#361L,_59#362L,_60#363L,_61#364L,_62#365L,_63#366L,_64#367L,_65#368L,_66#369L,_67#370L,_68#371L,_69#372L,_70#373L,_71#374L,_72#375L,_73#376L,_74#377L,_75#378L,_76#379L,_77#380L,_78#381L,_79#382L,_80#383L,_81#384L,_82#385L,_83#386L,_84#387L,_85#388L,_86#389L,_87#390L,_88#391L,_89#392L,_90#393L,_91#394L,_92#395L,_93#396L,_94#397L,_95#398L,_96#399L,_97#400L,_98#401L,_99#402L,_100#403L] parquet
<BLANKLINE>
== Analyzed Logical Plan ==
_1: bigint, _2: bigint, _3: bigint, _4: bigint, _5: bigint, _6: bigint, _7: bigint, _8: bigint, _9: bigint, _10: bigint, _11: bigint, _12: bigint, _13: bigint, _14: bigint, _15: bigint, _16: bigint, _17: bigint, _18: bigint, _19: bigint, _20: bigint, _21: bigint, _22: bigint, _23: bigint, _24: bigint, _25: bigint, _26: bigint, _27: bigint, _28: bigint, _29: bigint, _30: bigint, _31: bigint, _32: bigint, _33: bigint, _34: bigint, _35: bigint, _36: bigint, _37: bigint, _38: bigint, _39: bigint, _40: bigint, _41: bigint, _42: bigint, _43: bigint, _44: bigint, _45: bigint, _46: bigint, _47: bigint, _48: bigint, _49: bigint, _50: bigint, _51: bigint, _52: bigint, _53: bigint, _54: bigint, _55: bigint, _56: bigint, _57: bigint, _58: bigint, _59: bigint, _60: bigint, _61: bigint, _62: bigint, _63: bigint, _64: bigint, _65: bigint, _66: bigint, _67: bigint, _68: bigint, _69: bigint, _70: bigint, _71: bigint, _72: bigint, _73: bigint, _74: bigint, _75: bigint, _76: bigint, _77: bigint, _78: bigint, _79: bigint, _80: bigint, _81: bigint, _82: bigint, _83: bigint, _84: bigint, _85: bigint, _86: bigint, _87: bigint, _88: bigint, _89: bigint, _90: bigint, _91: bigint, _92: bigint, _93: bigint, _94: bigint, _95: bigint, _96: bigint, _97: bigint, _98: bigint, _99: bigint, _100: bigint
Project [_1#304L, _2#305L, _3#306L, _4#307L, _5#308L, _6#309L, _7#310L, _8#311L, _9#312L, _10#313L, _11#314L, _12#315L, _13#316L, _14#317L, _15#318L, _16#319L, _17#320L, _18#321L, _19#322L, _20#323L, _21#324L, _22#325L, _23#326L, _24#327L, _25#328L, _26#329L, _27#330L, _28#331L, _29#332L, _30#333L, _31#334L, _32#335L, _33#336L, _34#337L, _35#338L, _36#339L, _37#340L, _38#341L, _39#342L, _40#343L, _41#344L, _42#345L, _43#346L, _44#347L, _45#348L, _46#349L, _47#350L, _48#351L, _49#352L, _50#353L, _51#354L, _52#355L, _53#356L, _54#357L, _55#358L, _56#359L, _57#360L, _58#361L, _59#362L, _60#363L, _61#364L, _62#365L, _63#366L, _64#367L, _65#368L, _66#369L, _67#370L, _68#371L, _69#372L, _70#373L, _71#374L, _72#375L, _73#376L, _74#377L, _75#378L, _76#379L, _77#380L, _78#381L, _79#382L, _80#383L, _81#384L, _82#385L, _83#386L, _84#387L, _85#388L, _86#389L, _87#390L, _88#391L, _89#392L, _90#393L, _91#394L, _92#395L, _93#396L, _94#397L, _95#398L, _96#399L, _97#400L, _98#401L, _99#402L, _100#403L]
+- Relation[_1#304L,_2#305L,_3#306L,_4#307L,_5#308L,_6#309L,_7#310L,_8#311L,_9#312L,_10#313L,_11#314L,_12#315L,_13#316L,_14#317L,_15#318L,_16#319L,_17#320L,_18#321L,_19#322L,_20#323L,_21#324L,_22#325L,_23#326L,_24#327L,_25#328L,_26#329L,_27#330L,_28#331L,_29#332L,_30#333L,_31#334L,_32#335L,_33#336L,_34#337L,_35#338L,_36#339L,_37#340L,_38#341L,_39#342L,_40#343L,_41#344L,_42#345L,_43#346L,_44#347L,_45#348L,_46#349L,_47#350L,_48#351L,_49#352L,_50#353L,_51#354L,_52#355L,_53#356L,_54#357L,_55#358L,_56#359L,_57#360L,_58#361L,_59#362L,_60#363L,_61#364L,_62#365L,_63#366L,_64#367L,_65#368L,_66#369L,_67#370L,_68#371L,_69#372L,_70#373L,_71#374L,_72#375L,_73#376L,_74#377L,_75#378L,_76#379L,_77#380L,_78#381L,_79#382L,_80#383L,_81#384L,_82#385L,_83#386L,_84#387L,_85#388L,_86#389L,_87#390L,_88#391L,_89#392L,_90#393L,_91#394L,_92#395L,_93#396L,_94#397L,_95#398L,_96#399L,_97#400L,_98#401L,_99#402L,_100#403L] parquet
<BLANKLINE>
== Optimized Logical Plan ==
Relation[_1#304L,_2#305L,_3#306L,_4#307L,_5#308L,_6#309L,_7#310L,_8#311L,_9#312L,_10#313L,_11#314L,_12#315L,_13#316L,_14#317L,_15#318L,_16#319L,_17#320L,_18#321L,_19#322L,_20#323L,_21#324L,_22#325L,_23#326L,_24#327L,_25#328L,_26#329L,_27#330L,_28#331L,_29#332L,_30#333L,_31#334L,_32#335L,_33#336L,_34#337L,_35#338L,_36#339L,_37#340L,_38#341L,_39#342L,_40#343L,_41#344L,_42#345L,_43#346L,_44#347L,_45#348L,_46#349L,_47#350L,_48#351L,_49#352L,_50#353L,_51#354L,_52#355L,_53#356L,_54#357L,_55#358L,_56#359L,_57#360L,_58#361L,_59#362L,_60#363L,_61#364L,_62#365L,_63#366L,_64#367L,_65#368L,_66#369L,_67#370L,_68#371L,_69#372L,_70#373L,_71#374L,_72#375L,_73#376L,_74#377L,_75#378L,_76#379L,_77#380L,_78#381L,_79#382L,_80#383L,_81#384L,_82#385L,_83#386L,_84#387L,_85#388L,_86#389L,_87#390L,_88#391L,_89#392L,_90#393L,_91#394L,_92#395L,_93#396L,_94#397L,_95#398L,_96#399L,_97#400L,_98#401L,_99#402L,_100#403L] parquet
<BLANKLINE>
== Physical Plan ==
*(1) FileScan parquet [_1#304L,_2#305L,_3#306L,_4#307L,_5#308L,_6#309L,_7#310L,_8#311L,_9#312L,_10#313L,_11#314L,_12#315L,_13#316L,_14#317L,_15#318L,_16#319L,_17#320L,_18#321L,_19#322L,_20#323L,_21#324L,_22#325L,_23#326L,_24#327L,_25#328L,_26#329L,_27#330L,_28#331L,_29#332L,_30#333L,_31#334L,_32#335L,_33#336L,_34#337L,_35#338L,_36#339L,_37#340L,_38#341L,_39#342L,_40#343L,_41#344L,_42#345L,_43#346L,_44#347L,_45#348L,_46#349L,_47#350L,_48#351L,_49#352L,_50#353L,_51#354L,_52#355L,_53#356L,_54#357L,_55#358L,_56#359L,_57#360L,_58#361L,_59#362L,_60#363L,_61#364L,_62#365L,_63#366L,_64#367L,_65#368L,_66#369L,_67#370L,_68#371L,_69#372L,_70#373L,_71#374L,_72#375L,_73#376L,_74#377L,_75#378L,_76#379L,_77#380L,_78#381L,_79#382L,_80#383L,_81#384L,_82#385L,_83#386L,_84#387L,_85#388L,_86#389L,_87#390L,_88#391L,_89#392L,_90#393L,_91#394L,_92#395L,_93#396L,_94#397L,_95#398L,_96#399L,_97#400L,_98#401L,_99#402L,_100#403L] Batched: true, Format: Parquet, Location: InMemoryFileIndex[file:/path/test.parquet], PartitionFilters: [], PushedFilters: [], ReadSchema: struct<_1:bigint,_2:bigint,_3:bigint,_4:bigint,_5:bigint,_6:bigint,_7:bigint,_8:bigint,_9:bigint,...

由于某种原因,ReadSchema 属性总是被截断,而所有其他组件都适当地接受 spark.debug.maxToStringFields 设置。

【讨论】:

我知道simpleString 和非simpleString,但在这里都没有帮助。 explain(True) 除物理计划外还打印优化计划和逻辑计划,但字符限制仍然适用。 很奇怪 - 我们将 spark.debug.maxToStringFields 设置为 999999 并且成功输出了非常大的计划而没有任何截断。您确定在运行explain(True) 的上下文中正确应用了 SparkConf? 截断有 2 种类型,spark.debug.maxToStringFields 有助于一种类型。另一种类型是硬编码的,因此无法更改。如果您在我的问题中运行示例并将打印出来的内容粘贴到答案中,我可能会知道它是否有效。 啊,你说的是查询计划的ReadSchema 部分,而不是全部。该部分将始终在固定长度后被截断,这似乎与我见过的任何 conf 无关。请注意,查询计划的所有其他部分都会按预期响应maxToStringFields - 我将通过运行您的代码来更新答案。 没错。硬编码截断也适用于示例中未显示的“PartitionFilters”和“PushedFilters”。

以上是关于如何让pyspark显示整个查询计划而不是......如果有很多字段?的主要内容,如果未能解决你的问题,请参考以下文章

默认情况下,条件使用“内连接”而不是“左连接”方法使我的查询工作不是我计划的方式

如何在 Mathematica 中显示求和函数而不是扩展整个系列?

PySpark - 为单个行而不是所有记录设置/删除列

如何显示存储过程的执行计划?

Spring Boot计划任务运行整个小时而不是一小时

ios 8 如何让整个应用伸展到更大的屏幕而不是自动布局