requirement failed: Block broadcast_487 is already present in the MemoryStore

本文详细解析了SparkSQL在执行过程中遇到的两个常见错误:Block已在MemoryStore中存在和磁盘空间不足的问题。通过调整Hadoop集群的配置,移除swap分区作为yarn的日志目录,成功解决了Spark任务执行失败的情况。

场景:

以往正常执行的sparksql,今天在公司执行报如下错误:

第一次执行报错如下:

Caused by: java.sql.SQLException: org.apache.spark.SparkException: Job aborted due to stage failure: Task 1382 in stage 320.0 failed 4 times, most recent failure: Lost task 1382.3 in stage 320.0 (TID 165140, leaptest-dn03.bjev.com): java.io.IOException: java.lang.IllegalArgumentException: requirement failed: Block broadcast_487 is already present in the MemoryStore
    at org.apache.spark.util.Utils$.tryOrIOException(Utils.scala:1258)
    at org.apache.spark.broadcast.TorrentBroadcast.readBroadcastBlock(TorrentBroadcast.scala:174)
    at org.apache.spark.broadcast.TorrentBroadcast._value$lzycompute(TorrentBroadcast.scala:65)
    at org.apache.spark.broadcast.TorrentBroadcast._value(TorrentBroadcast.scala:65)
    at org.apache.spark.broadcast.TorrentBroadcast.getValue(TorrentBroadcast.scala:89)
    at org.apache.spark.broadcast.Broadcast.value(Broadcast.scala:70)
    at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:72)
    at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:47)
    at org.apache.spark.scheduler.Task.run(Task.scala:85)
    at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:283)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalArgumentException: requirement failed: Block broadcast_487 is already present in the MemoryStore
    at scala.Predef$.require(Predef.scala:224)
    at org.apache.spark.storage.memory.MemoryStore.putIteratorAsValues(MemoryStore.scala:182)
    at org.apache.spark.storage.BlockManager$$anonfun$doPutIterator$1.apply(BlockManager.scala:919)
    at org.apache.spark.storage.BlockManager$$anonfun$doPutIterator$1.apply(BlockManager.scala:910)
    at org.apache.spark.storage.BlockManager.doPut(BlockManager.scala:866)
    at org.apache.spark.storage.BlockManager.doPutIterator(BlockManager.scala:910)
    at org.apache.spark.storage.BlockManager.putIterator(BlockManager.scala:700)
    at org.apache.spark.storage.BlockManager.putSingle(BlockManager.scala:1213)
    at org.apache.spark.broadcast.TorrentBroadcast$$anonfun$readBroadcastBlock$1.apply(TorrentBroadcast.scala:194)
    at org.apache.spark.util.Utils$.tryOrIOException(Utils.scala:1251)
    ... 12 more

Driver stacktrace:
    at org.apache.hive.jdbc.HiveStatement.execute(HiveStatement.java:279)
    at com.lenovo.lps.farseer.priest2.ext.SparkExecDao.executeOneSql(SparkExecDao.java:370)
    at com.lenovo.lps.farseer.priest2.ext.SparkExecDao.executeOneSqlProxy(SparkExecDao.java:341)
    at com.lenovo.lps.farseer.priest2.ext.SparkExecDao.sparkExecute(SparkExecDao.java:255)
    ... 82 more


 

多次执行的时候偶尔会报另一个错误:

Caused by: java.sql.SQLException: org.apache.spark.SparkException: Job aborted due to stage failure: Task 3 in stage 342.0 failed 4 times, most recent failure: Lost task 3.3 in stage 342.0 (TID 166270, leaptest-dn03.bjev.com): java.io.FileNotFoundException: /swap/hadoop/yarn/local/usercache/hive/appcache/application_1557227972884_8104/blockmgr-927821e7-2b49-465d-86de-9d2707e2517b/03/temp_shuffle_be51c50d-54ac-4a5b-b840-59129fed41fb (设备上没有空间)
    at java.io.FileOutputStream.open(Native Method)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:221)
    at org.apache.spark.storage.DiskBlockObjectWriter.open(DiskBlockObjectWriter.scala:88)
    at org.apache.spark.storage.DiskBlockObjectWriter.write(DiskBlockObjectWriter.scala:181)
    at org.apache.spark.shuffle.sort.BypassMergeSortShuffleWriter.write(BypassMergeSortShuffleWriter.java:150)
    at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:79)
    at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:47)
    at org.apache.spark.scheduler.Task.run(Task.scala:85)
    at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:283)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

Driver stacktrace:
    at org.apache.hive.jdbc.HiveStatement.execute(HiveStatement.java:279)
    at com.lenovo.lps.farseer.priest2.ext.SparkExecDao.executeOneSql(SparkExecDao.java:370)
    at com.lenovo.lps.farseer.priest2.ext.SparkExecDao.executeOneSqlProxy(SparkExecDao.java:341)
    at com.lenovo.lps.farseer.priest2.ext.SparkExecDao.sparkExecute(SparkExecDao.java:255)
    ... 100 more

从第二次的异常中发现,部署hadoop集群的时候,实施人员将swap分区的目录配置成yarn的任务日志目录中了。即yarn-site.xml的这两个配置(yarn.nodemanager.log-dirs和yarn.nodemanager.local-dirs)。

    <property>
      <name>yarn.nodemanager.local-dirs</name>
      <value>/swap/hadoop/yarn/local,/data/hadoop/yarn/local</value>
    </property>

    <property>
      <name>yarn.nodemanager.log-dirs</name>
      <value>/swap/hadoop/yarn/log,/data/hadoop/yarn/log</value>
    </property>

在配置中去掉swap分区目录,重启yarn即可:

    <property>
      <name>yarn.nodemanager.local-dirs</name>
      <value>/data/hadoop/yarn/local</value>
    </property>

    <property>
      <name>yarn.nodemanager.log-dirs</name>
      <value>/data/hadoop/yarn/log</value>
    </property>

<think>好的,我现在需要帮用户分析Spark广播变量的存储与移除日志信息。首先,我得回忆一下Spark广播变量的基本机制。广播变量允许将只读变量缓存在每个工作节点上,避免重复发送数据,这对性能优化很重要。用户提到想分析相关的存储和移除日志,所以我得先确定这些日志的位置和内容。 接下来,存储机制方面,广播变量在Driver端创建后会通过Block Manager分发到各个Executor。这时候Driver的日志里可能会有关于广播变量创建和分发的记录。比如,当调用sc.broadcast()时,Driver会生成一个唯一的广播ID,并记录在日志中。同时,Executor在接收到广播数据时,应该也会有日志条目,比如在Block Manager中存储的细节。我需要确认这些日志的级别,通常INFO级别可能不够详细,可能需要调整到DEBUG级别来获取更多信息。 然后是移除机制。广播变量在不再被引用时会被自动移除,但具体什么时候触发呢?可能是在任务执行完毕后,或者依赖的RDD被销毁时。Driver和Executor的日志中应该会有相关的移除记录,比如释放资源的条目。用户可能需要检查这些日志来确认移除的时间和条件,避免内存泄漏。 接下来是日志分析的具体步骤。首先,用户需要启用更详细的日志级别,可能在spark-submit时配置--driver-java-options和--executor-java-options来设置log4j的级别为DEBUG。然后,在Driver日志中搜索Broadcast相关的条目,比如创建广播变量、存储到Block Manager的事件。Executor的日志里会有接收和缓存广播变量的记录,以及任务结束后移除的条目。 工具方面,可以使用grep或者ELK这样的日志分析工具来过滤关键字,如“Broadcast”、“Destroying broadcast”等。另外,Spark UI也可能提供一些可视化信息,比如Storage标签页里的广播变量状态,这对用户来说可能更直观。 可能遇到的问题包括日志信息过于分散,或者级别不够详细,需要调整配置。此外,用户可能需要检查是否有未正确释放的广播变量,这时候需要分析引用情况,比如是否调用了unpersist()方法。如果广播变量没有被移除,可能会导致内存占用过高,影响性能。 最后,用户可能需要优化广播变量的使用,比如及时手动释放不再需要的变量,或者调整序列化方式减少存储开销。这部分需要结合具体应用场景,可能需要进一步的问题来深入探讨。</think>### Spark广播变量存储与移除日志分析指南 #### 一、存储机制分析 1. **Driver端存储日志** - 广播变量创建时生成唯一ID:`INFO BlockManagerInfo: Added broadcast_0 in memory` - 数据分片存储记录:`DEBUG BlockManager: Put block broadcast_0_piece0` - 元数据存储位置:`/spark/blockmgr-xxx` 目录下可见具体存储文件[^1] 2. **Executor端接收日志** - 接收广播数据日志:`INFO BlockManager: Getting broadcast_0` - 数据校验记录:`DEBUG TorrentBroadcast: Reading broadcast variable 0 took 236 ms` #### 二、移除机制分析 1. **自动移除触发条件** - 任务执行完成时:`INFO ContextCleaner: Cleaned broadcast_0` - 引用计数归零时:`DEBUG BlockManager: Removing broadcast 0` - 内存压力触发:`WARN MemoryStore: Not enough space to cache broadcast_1` 2. **手动移除方法** ```scala val broadcastVar = sc.broadcast(data) // 使用后手动释放 broadcastVar.unpersist() // 强制删除 broadcastVar.destroy() ``` #### 三、日志分析步骤 1. **启用详细日志** ```bash spark-submit --conf "spark.driver.extraJavaOptions=-Dlog4j.configuration=file:log4j-debug.properties" --conf "spark.executor.extraJavaOptions=-Dlog4j.configuration=file:log4j-debug.properties" ``` 2. **关键日志模式匹配 ``` grep "Broadcast\|BlockManager\|MemoryStore" spark.log ``` 3. **生命周期跟踪示例 ``` 09:00:01 INFO TorrentBroadcast: Started reading broadcast variable 0 09:00:03 INFO BlockManager: Stored broadcast_0 (piece 0) 09:05:22 INFO ContextCleaner: Cleaned broadcast 0 ``` #### 四、常见问题诊断表 | 现象 | 可能原因 | 解决方案 | |------|--------|----------| | 广播变量未移除 | 强引用持有 | 检查代码中的变量引用 | | 存储时间过长 | 序列化问题 | 使用Kryo序列化 | | 内存不足警告 | 广播数据过大 | 优化数据结构 | #### 五、性能优化建议 1. 监控广播变量内存占用: ```scala val storageStatus = sc.getExecutorStorageStatus storageStatus.foreach { status => println(s"${status.blockManagerId} => ${status.memUsed}") } ``` 2. 使用压缩传输: ```scala spark.conf.set("spark.broadcast.compress", "true") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值